gohugoio/hugo · error

failed to read directory

Error message

failed to read directory %q: %s

What it means

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.

Solutions

  1. Confirm the path is actually a directory and not a file (use os.FileExists or check in the shell).
  2. Check filesystem permissions on the target directory and all parent directories.
  3. Ensure the path is within the Hugo working directory (workFs = BaseFs.WorkDir).
  4. Read the %s detail in the error to determine if it is permission-related or an afero internal error.

Example fix

// before:
{{ $files := os.ReadDir "data/config.json" }}
// after (point at the directory, not a file):
{{ $files := os.ReadDir "data" }}
Defensive patterns

Strategy: validation

Validate before calling

{{ $info := os.ReadDir $path }}
{{ with $err }}{{ warnf "ReadDir failed: %s" $err }}{{ end }}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09). Data as JSON: /api/errors/601dbf054c13be89. Report an issue: GitHub.

Appendix: source

Thrown at tpl/os/os.go:125

	if err != nil && herrors.IsNotExist(err) {
		return "", nil
	}
	return s, err
}

// ReadDir lists the directory contents relative to the configured WorkingDir.
func (ns *Namespace) ReadDir(i any) ([]_os.FileInfo, error) {
	path, err := cast.ToStringE(i)
	if err != nil {
		return nil, err
	}

	list, err := afero.ReadDir(ns.workFs, path)
	if err != nil {
		if herrors.IsNotExist(err) {
			return nil, nil
		}
		return nil, fmt.Errorf("failed to read directory %q: %s", path, err)
	}

	return list, nil
}

// FileExists checks whether a file exists under the given path.
func (ns *Namespace) FileExists(i any) (bool, error) {
	path, err := cast.ToStringE(i)
	if err != nil {
		return false, err
	}

	if path == "" {
		return false, nil
	}

	status, err := afero.Exists(ns.readFileFs, path)
	if err != nil {

View on GitHub (pinned to 52c9bd7908)