{"id":"2cb025081b61fd50","repo":"gohugoio/hugo","slug":"s-q-mount-source-must-be-a-local-path-for-modu","errorCode":null,"errorMessage":"%s: %q: mount source must be a local path for modules/themes","messagePattern":"(.+?): %q: mount source must be a local path for modules/themes","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/collect.go","lineNumber":819,"sourceCode":"\t\t}\n\n\t\t// Special case for node_modules imports in themes/modules.\n\t\t// See #14089.\n\t\tvar isModuleNodeModulesImport bool\n\t\tif !owner.projectMod {\n\t\t\tnodeModulesImportSource := c.nodeModulesRoot(mnt.Source)\n\t\t\tif nodeModulesImportSource != \"\" {\n\t\t\t\tisModuleNodeModulesImport = true\n\t\t\t\tmnt.Source = nodeModulesImportSource\n\t\t\t}\n\t\t}\n\n\t\tmnt.Source = filepath.Clean(mnt.Source)\n\t\tmnt.Target = filepath.Clean(mnt.Target)\n\t\tvar sourceDir string\n\n\t\tif !owner.projectMod && !filepath.IsLocal(mnt.Source) {\n\t\t\treturn nil, fmt.Errorf(\"%s: %q: mount source must be a local path for modules/themes\", errMsg, mnt.Source)\n\t\t}\n\t\tif filepath.IsAbs(mnt.Source) {\n\t\t\t// Abs paths in the main project is allowed.\n\t\t\tsourceDir = mnt.Source\n\t\t} else {\n\t\t\tsourceDir = filepath.Join(dir, mnt.Source)\n\t\t}\n\n\t\t// Verify that Source exists\n\t\t_, err := c.fs.Stat(sourceDir)\n\t\tif err != nil {\n\t\t\tif paths.IsSameFilePath(sourceDir, c.ccfg.PublishDir) {\n\t\t\t\t// This is a little exotic, but there are use cases for mounting the public folder.\n\t\t\t\t// This will typically also be in .gitingore, so create it.\n\t\t\t\tif err := c.fs.MkdirAll(sourceDir, 0o755); err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"%s: %q\", errMsg, err)\n\t\t\t\t}\n\t\t\t} else if strings.HasSuffix(sourceDir, files.FilenameHugoStatsJSON) {","sourceCodeStart":801,"sourceCodeEnd":837,"githubUrl":"https://github.com/gohugoio/hugo/blob/8a468df065a75c1c7cf9f6850f32148746590ea5/modules/collect.go#L801-L837","documentation":"Hugo's module collector rejects a mount whose `source` escapes the module's own directory when the mount is declared by a theme or module (not the main project). Only the project module may use absolute or non-local (e.g. `../`) source paths; modules/themes are sandboxed to their own tree (with a special carve-out for node_modules imports). This is enforced in normalizeMounts via filepath.IsLocal.","triggerScenarios":"A theme's or imported module's hugo.toml/config declares `[[module.mounts]]` with `source` set to an absolute path (`/var/data`) or a path traversing upward (`../shared/content`), then Hugo builds and collects module configs. The main project itself is exempt (`owner.projectMod`).","commonSituations":"Copying a project-level mounts config into a theme's config file; a theme author trying to mount files from outside the theme repo; Windows drive-letter absolute paths in a theme mount; older configs that relied on looser validation before filepath.IsLocal enforcement.","solutions":["Change the mount source in the theme/module config to a path relative to and inside the module directory (e.g. `source = \"assets\"`).","If the files live outside the module, move the mount declaration into the main project's config, where absolute paths are allowed.","For node_modules packages, use a `node_modules/<pkg>/...` source, which Hugo special-cases for modules (see issue #14089).","Vendor or copy the external files into the module so the source becomes local."],"exampleFix":"# theme/hugo.toml — before\n[[module.mounts]]\nsource = \"../shared/assets\"\ntarget = \"assets\"\n\n# after (declare in the project's hugo.toml instead)\n[[module.mounts]]\nsource = \"/abs/or/relative/shared/assets\"\ntarget = \"assets\"","handlingStrategy":"validation","validationCode":"// before calling modules.Collect / hugo build\nfor _, m := range cfg.Mounts {\n    if strings.HasPrefix(m.Source, \"/\") || filepath.IsAbs(m.Source) || strings.Contains(m.Source, \"://\") {\n        return fmt.Errorf(\"mount source %q must be a relative local path\", m.Source)\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["In module config, always use relative paths (e.g. \"content\", \"assets/scss\") for mount sources, never absolute paths or URLs","Lint hugo.toml/module config in CI with a schema check before building","If you need external content, import it as a module and mount from that module rather than pointing source outside the project"],"tags":["hugo-modules","config","mounts","path-validation"],"analyzedSha":"8a468df065a75c1c7cf9f6850f32148746590ea5","analyzedAt":"2026-07-31T21:23:07.045Z","schemaVersion":2}