{"record":{"id":"601dbf054c13be89","repo":"gohugoio/hugo","slug":"failed-to-read-directory-q-s","errorCode":null,"errorMessage":"failed to read directory %q: %s","messagePattern":"failed to read directory %q: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tpl/os/os.go","lineNumber":125,"sourceCode":"\tif err != nil && herrors.IsNotExist(err) {\n\t\treturn \"\", nil\n\t}\n\treturn s, err\n}\n\n// ReadDir lists the directory contents relative to the configured WorkingDir.\nfunc (ns *Namespace) ReadDir(i any) ([]_os.FileInfo, error) {\n\tpath, err := cast.ToStringE(i)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlist, err := afero.ReadDir(ns.workFs, path)\n\tif err != nil {\n\t\tif herrors.IsNotExist(err) {\n\t\t\treturn nil, nil\n\t\t}\n\t\treturn nil, fmt.Errorf(\"failed to read directory %q: %s\", path, err)\n\t}\n\n\treturn list, nil\n}\n\n// FileExists checks whether a file exists under the given path.\nfunc (ns *Namespace) FileExists(i any) (bool, error) {\n\tpath, err := cast.ToStringE(i)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tif path == \"\" {\n\t\treturn false, nil\n\t}\n\n\tstatus, err := afero.Exists(ns.readFileFs, path)\n\tif err != nil {","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/gohugoio/hugo/blob/52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2/tpl/os/os.go#L107-L143","documentation":"Thrown by os.ReadDir when afero.ReadDir fails on a path that is NOT a not-exist error. Note that genuinely missing directories return (nil, nil) silently; this error fires only for other I/O failures. The %q is the path and %s is the underlying error string.","triggerScenarios":"Calling {{ os.ReadDir \"some/path\" }} in a template where the path exists as a file (not a directory), has restrictive permissions, or triggers an afero filesystem error other than ENOENT.","commonSituations":"Pointing ReadDir at a file rather than a directory, permission denied on the working directory, a corrupted overlay filesystem entry, or a path that violates Hugo's filesystem sandbox boundaries. The workFs is the WorkDir drop-symlinks filesystem, so symlinks pointing outside are silently dropped rather than erroring here.","solutions":["Confirm the path is actually a directory and not a file (use os.FileExists or check in the shell).","Check filesystem permissions on the target directory and all parent directories.","Ensure the path is within the Hugo working directory (workFs = BaseFs.WorkDir).","Read the %s detail in the error to determine if it is permission-related or an afero internal error."],"exampleFix":"// before:\n{{ $files := os.ReadDir \"data/config.json\" }}\n// after (point at the directory, not a file):\n{{ $files := os.ReadDir \"data\" }}","handlingStrategy":"validation","validationCode":"{{ $info := os.ReadDir $path }}\n{{ with $err }}{{ warnf \"ReadDir failed: %s\" $err }}{{ end }}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Verify the path is a directory (not a file) before calling os.ReadDir.","Keep ReadDir targets inside the Hugo working directory.","Prefer os.FileExists for single-file checks and use ReadDir only for directories."],"tags":["os","filesystem","readdir","template"],"backgroundTag":null,"analyzedSha":"52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2","analyzedAt":"2026-08-09T21:49:36.660Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}