{"id":"3c156b16026cda5e","repo":"gohugoio/hugo","slug":"failed-to-read-dir-q-q","errorCode":null,"errorMessage":"failed to read dir %q: %q","messagePattern":"failed to read dir %q: %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/collect.go","lineNumber":679,"sourceCode":"func (c *collector) mountCommonJSConfig(owner *moduleAdapter, mounts []Mount) ([]Mount, error) {\n\tfor _, m := range mounts {\n\t\tif strings.HasPrefix(m.Target, files.JsConfigFolderMountPrefix) {\n\t\t\t// This follows the convention of the other component types (assets, content, etc.),\n\t\t\t// if one or more is specified by the user, we skip the defaults.\n\t\t\t// These mounts were added to Hugo in 0.75.\n\t\t\treturn mounts, nil\n\t\t}\n\t}\n\n\t// Mount the common JS config files.\n\td, err := c.fs.Open(owner.Dir())\n\tif err != nil {\n\t\treturn mounts, fmt.Errorf(\"failed to open dir %q: %q\", owner.Dir(), err)\n\t}\n\tdefer d.Close()\n\tfis, err := d.(fs.ReadDirFile).ReadDir(-1)\n\tif err != nil {\n\t\treturn mounts, fmt.Errorf(\"failed to read dir %q: %q\", owner.Dir(), err)\n\t}\n\n\thasPackageHugoJSON := false\n\tfor _, fi := range fis {\n\t\tif fi.Name() == files.FilenamePackageHugoJSON {\n\t\t\thasPackageHugoJSON = true\n\t\t\tbreak\n\t\t}\n\t}\n\n\tfor _, fi := range fis {\n\t\tn := fi.Name()\n\n\t\tshould := n == files.FilenamePackageHugoJSON || (n == files.FilenamePackageJSON && !hasPackageHugoJSON)\n\t\tshould = should || commonJSConfigs.MatchString(n)\n\n\t\tif should {\n\t\t\tmounts = append(mounts, Mount{","sourceCodeStart":661,"sourceCodeEnd":697,"githubUrl":"https://github.com/gohugoio/hugo/blob/8a468df065a75c1c7cf9f6850f32148746590ea5/modules/collect.go#L661-L697","documentation":"Immediately after opening a module's root directory, `mountCommonJSConfig` (modules/collect.go:679) lists its entries with `ReadDir(-1)` to look for common JS config files and `package.hugo.json`. If the directory listing fails, Hugo reports `failed to read dir %q: %q`. The open succeeded, so this indicates a failure while enumerating entries rather than reaching the path.","triggerScenarios":"`ReadDir(-1)` failing on the module root during collection: I/O errors from the underlying filesystem, the path being a plain file so the afero handle doesn't support directory reads, or the directory being removed between Open and ReadDir.","commonSituations":"Flaky network filesystems (NFS/SMB/overlay in containers) erroring mid-listing; a replacement or theme path that is actually a file; antivirus or sandboxing software interfering with directory enumeration on Windows; extremely broken symlink farms inside the module dir.","solutions":["Confirm the quoted path is a real, listable directory: `ls <dir>`.","Fix module/replacement config if the path resolves to a file instead of a directory.","On network or containerized filesystems, retry on stable local storage to rule out I/O flakiness.","If the module cache is corrupted, run `hugo mod clean` and re-fetch modules."],"exampleFix":"# before\n[module]\nreplacements = \"github.com/org/theme -> ./theme.zip\"  # a file, not a dir\n# after\nunzip theme.zip -d ./theme\n# config:\n# replacements = \"github.com/org/theme -> ./theme\"","handlingStrategy":"try-catch","validationCode":"if _, err := os.ReadDir(dir); err != nil {\n    return fmt.Errorf(\"pre-flight readdir failed for %q: %w\", dir, err)\n}","typeGuard":"func isReadDirFailure(err error) bool {\n    var pe *fs.PathError\n    return errors.As(err, &pe) && pe.Op == \"readdirent\"\n}","tryCatchPattern":"if err := collect(); err != nil {\n    var pe *fs.PathError\n    if errors.As(err, &pe) {\n        log.Printf(\"cannot read %s: %v\", pe.Path, pe.Err)\n    }\n    return err\n}","preventionTips":["Grant read+execute permission on all directories under content/layouts/assets and the module cache","Watch for directories deleted mid-build (race with file watchers or CI cleanup steps)","On network filesystems, prefer copying the module cache locally to avoid transient read errors"],"tags":["hugo-modules","filesystem","io","js-config"],"analyzedSha":"8a468df065a75c1c7cf9f6850f32148746590ea5","analyzedAt":"2026-07-31T21:23:07.045Z","schemaVersion":2}