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
- Check the quoted directory exists and is readable: `ls -la <dir>`.
- If it's under the Go module cache and corrupted, run `hugo mod clean` (or `go clean -modcache`) and re-download.
- Fix any `module.replacements` entry pointing at a file or nonexistent path.
- 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
- Ensure directories referenced by modules/mounts exist with read permission for the build user
- In containers, check volume mounts and ownership (UID mapping) for module cache dirs
- Avoid symlinks pointing outside the allowed workspace — Hugo's security model may block them
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
- failed to read dir %q: %q
- failed to save file %q:: %w
- failed to save file %q: %s
- failed to create workingDir: %w
- failed to open non-content file: %w
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/fdbc67fb73d7e5aa.json.
Report an issue: GitHub ↗.