{"id":"74772cb5edab2d12","repo":"gohugoio/hugo","slug":"invalid-filename","errorCode":null,"errorMessage":"invalid filename","messagePattern":"invalid filename","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tpl/os/os.go","lineNumber":82,"sourceCode":"func (ns *Namespace) Getenv(key any) (string, error) {\n\tskey, err := cast.ToStringE(key)\n\tif err != nil {\n\t\treturn \"\", nil\n\t}\n\n\tif err = ns.deps.ExecHelper.Sec().CheckAllowedGetEnv(skey); err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn _os.Getenv(skey), nil\n}\n\n// readFile reads the file named by filename in the given filesystem\n// and returns the contents as a string.\nfunc readFile(fs afero.Fs, filename string) (string, error) {\n\tfilename = filepath.Clean(filename)\n\tif filename == \"\" || filename == \".\" || filename == string(_os.PathSeparator) {\n\t\treturn \"\", errors.New(\"invalid filename\")\n\t}\n\n\tb, err := afero.ReadFile(fs, filename)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn string(b), nil\n}\n\n// ReadFile reads the file named by filename relative to the configured WorkingDir.\n// It returns the contents as a string.\n// There is an upper size limit set at 1 megabytes.\nfunc (ns *Namespace) ReadFile(i any) (string, error) {\n\ts, err := cast.ToStringE(i)\n\tif err != nil {\n\t\treturn \"\", err\n\t}","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/gohugoio/hugo/blob/8a468df065a75c1c7cf9f6850f32148746590ea5/tpl/os/os.go#L64-L100","documentation":"The `os.ReadFile` template function cleans the given path and rejects it if the result is empty, \".\", or the bare path separator — i.e. it doesn't name an actual file. This guards against reading the working directory root or a meaningless path.","triggerScenarios":"`{{ os.ReadFile \"\" }}`, `{{ os.ReadFile \".\" }}`, `{{ os.ReadFile \"/\" }}`, or a path that filepath.Clean collapses to one of those (e.g. \"./\", \"foo/..\").","commonSituations":"A template variable used as the filename is empty because a page param or site config value is unset; string concatenation producing \"\" or \"/\"; iterating over params where some entries lack a path field.","solutions":["Guard the call: `{{ with $path }}{{ os.ReadFile . }}{{ end }}` so empty values are skipped.","Check where the path variable comes from (front matter param, site config) and set or default it (`default \"data/x.txt\" .Params.file`).","Ensure the path points to a real file relative to the project working directory, not a directory."],"exampleFix":"{{/* before */}}\n{{ $c := os.ReadFile .Params.snippet }}\n{{/* after */}}\n{{ with .Params.snippet }}{{ $c := os.ReadFile . }}{{ end }}","handlingStrategy":"validation","validationCode":"{{ $path := \"data/notes.txt\" }}\n{{ if and $path (fileExists $path) }}\n  {{ $content := readFile $path }}\n{{ end }}","typeGuard":null,"tryCatchPattern":"{{ with try (os.ReadFile $path) }}\n  {{ with .Err }}{{ warnf \"readFile %q: %s\" $path . }}{{ else }}{{ $content := .Value }}{{ end }}\n{{ end }}","preventionTips":["Never pass an empty or nil path to os.ReadFile/os.FileExists/os.Stat — guard with {{ with $path }} first","Use project-relative paths inside the project root; Hugo's security model rejects paths that escape it (e.g. ../../ traversal)","Gate reads behind os.FileExists so optional files don't break the build"],"tags":["hugo","templates","os","file-io"],"analyzedSha":"8a468df065a75c1c7cf9f6850f32148746590ea5","analyzedAt":"2026-07-31T21:23:07.045Z","schemaVersion":2}