gohugoio/hugo · error

failed to open dir

Error message

failed to open dir %q: %q

What it means

mountCommonJSConfig opens owner.Dir() with c.fs.Open to scan for JS config files (babel/postcss/tailwind.config.js, package.hugo.json); if the open fails for a reason other than not-exist (which is tolerated elsewhere), the raw error is surfaced. Indicates a filesystem/permission problem on an already-resolved module directory.

Solutions

  1. Check permissions on the dir named in the error: `ls -ld <dir>` and chmod/chown as needed.
  2. Resolve broken symlinks inside the module/theme dir.
  3. For cache dirs, `go clean -modcache` then rebuild to restore a clean tree.
  4. In containers, confirm the volume mount actually includes the module dir.

Example fix

// before: theme dir not readable
chmod -R go-rx themes/mytheme
// after: restore read+execute on the dir
chmod -R a+rX themes/mytheme
Defensive patterns

Strategy: validation

Validate before calling

// Ensure module/theme dirs are openable before build
func ensureDirOpenable(dir string) error {
  f, err := os.Open(dir)
  if err != nil { return fmt.Errorf("cannot open %s: %w", dir, err) }
  return f.Close()
}

Type guard

null

Try / catch

// if errors.Is(err, fs.ErrPermission) { chmod + retry }

Prevention

When it happens

Trigger: A module whose Dir() exists in metadata but cannot be opened — permission denied, symlink loop, removed between resolution and scan, or an afero filesystem layer rejecting the open.

Common situations: Permission drift on the module cache/theme dir, a broken symlink in a theme, network filesystem hiccup, or a container mount that omits the dir.

Related errors


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

Appendix: 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 52c9bd7908)