gohugoio/hugo · error

failed to open dir %q: %q

Error message

failed to open dir %q: %q

What it means

`mountCommonJSConfig` (modules/collect.go:674) auto-mounts JS tooling configs (`postcss.config.js`, `babel.config.js`, `tailwind.config.js`, `package.hugo.json`) from a module's root when the user hasn't mounted them explicitly. To do so it opens the module's directory; if `Open` fails, Hugo reports `failed to open dir %q: %q` with the directory and underlying error.

Source

Thrown at modules/collect.go:674

}

// Matches postcss.config.js etc.
var commonJSConfigs = regexp.MustCompile(`^(babel|postcss|tailwind)\.config\.(c|m)?js$`)

func (c *collector) mountCommonJSConfig(owner *moduleAdapter, mounts []Mount) ([]Mount, error) {
	for _, m := range mounts {
		if strings.HasPrefix(m.Target, files.JsConfigFolderMountPrefix) {
			// This follows the convention of the other component types (assets, content, etc.),
			// if one or more is specified by the user, we skip the defaults.
			// These mounts were added to Hugo in 0.75.
			return mounts, nil
		}
	}

	// Mount the common JS config files.
	d, err := c.fs.Open(owner.Dir())
	if err != nil {
		return mounts, fmt.Errorf("failed to open dir %q: %q", owner.Dir(), err)
	}
	defer d.Close()
	fis, err := d.(fs.ReadDirFile).ReadDir(-1)
	if err != nil {
		return mounts, fmt.Errorf("failed to read dir %q: %q", owner.Dir(), err)
	}

	hasPackageHugoJSON := false
	for _, fi := range fis {
		if fi.Name() == files.FilenamePackageHugoJSON {
			hasPackageHugoJSON = true
			break
		}
	}

	for _, fi := range fis {
		n := fi.Name()

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Check the quoted directory exists and is readable: `ls -la <dir>`.
  2. If it's under the Go module cache and corrupted, run `hugo mod clean` (or `go clean -modcache`) and re-download.
  3. Fix any `module.replacements` entry pointing at a file or nonexistent path.
  4. In containers, ensure the module cache volume is mounted with read access for the build user.

Example fix

# before: cache dir unreadable in container
docker run -v gocache:/root/go ... hugo
# after: fix ownership/permissions
docker run -v gocache:/root/go -u root ... sh -c 'chmod -R a+rX /root/go/pkg/mod && hugo'
Defensive patterns

Strategy: validation

Validate before calling

if fi, err := os.Stat(dir); err != nil {
    return fmt.Errorf("dir %q missing: %w", dir, err)
} else if !fi.IsDir() {
    return fmt.Errorf("%q is not a directory", dir)
}

Type guard

func isPermOrNotExist(err error) bool {
    return errors.Is(err, fs.ErrNotExist) || errors.Is(err, fs.ErrPermission)
}

Try / catch

if err := walk(dir); err != nil {
    if errors.Is(err, fs.ErrPermission) { /* fix perms, don't retry */ }
    return err
}

Prevention

When it happens

Trigger: Module collection calling `c.fs.Open(owner.Dir())` on a module whose resolved root directory can't be opened — deleted after resolution, permission denied, or a path that exists as a file rather than a directory (e.g. broken replacement mapping).

Common situations: Restrictive permissions on Go module cache dirs (the cache is read-only, but ACL/umask oddities or containers mounting it wrong can break reads); a `module.replacements` target pointing at a file; network/overlay filesystems dropping the directory mid-build.

Related errors


AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31). Data as JSON: /data/errors/fdbc67fb73d7e5aa.json. Report an issue: GitHub ↗.