{"id":"a9934fa8f04208ae","repo":"gohugoio/hugo","slug":"invalid-module-path-q-must-be-relative-to-themes","errorCode":null,"errorMessage":"invalid module path %q; must be relative to themesDir when defined outside of the project","messagePattern":"invalid module path %q; must be relative to themesDir when defined outside of the project","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/client.go","lineNumber":878,"sourceCode":"\t}\n\n\tif goModOnly {\n\t\treturn nil\n\t}\n\n\tif err := c.rewriteGoMod(goSumFilename, isGoMod); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (c *Client) shouldNotVendor(path string) bool {\n\treturn c.noVendor != nil && c.noVendor.Match(path)\n}\n\nfunc (c *Client) createThemeDirname(modulePath string, isProjectMod bool) (string, error) {\n\tinvalid := fmt.Errorf(\"invalid module path %q; must be relative to themesDir when defined outside of the project\", modulePath)\n\n\tmodulePath = filepath.Clean(modulePath)\n\tif filepath.IsAbs(modulePath) {\n\t\tif isProjectMod {\n\t\t\treturn modulePath, nil\n\t\t}\n\t\treturn \"\", invalid\n\t}\n\n\tmoduleDir := filepath.Join(c.ccfg.ThemesDir, modulePath)\n\tif !isProjectMod && !strings.HasPrefix(moduleDir, c.ccfg.ThemesDir) {\n\t\treturn \"\", invalid\n\t}\n\treturn moduleDir, nil\n}\n\n// ClientConfig configures the module Client.\ntype ClientConfig struct {","sourceCodeStart":860,"sourceCodeEnd":896,"githubUrl":"https://github.com/gohugoio/hugo/blob/8a468df065a75c1c7cf9f6850f32148746590ea5/modules/client.go#L860-L896","documentation":"`createThemeDirname` (modules/client.go:877) maps a non-Go-module import path to a directory under `themesDir`. For imports declared by a module other than the project itself, the resolved path must stay inside `themesDir`: absolute paths and relative paths that escape via `..` are rejected. This is a path-traversal guard — a third-party theme module must not be able to point Hugo at arbitrary filesystem locations.","triggerScenarios":"Module collection where a non-project module (a theme/component you import) declares an import whose path is absolute (e.g. `/home/me/theme`) or resolves outside themesDir after `filepath.Clean` (e.g. `../../other`), and the path was not replaced via a project-level `replace` directive.","commonSituations":"Copying a theme's dev config that used absolute local paths into a published module; using `../sibling-theme` relative imports inside a nested module; expecting `module.replacements` semantics but declaring the local path directly in the imported module's config instead of the project's.","solutions":["In the imported module's config, reference the theme by its module path or by a name relative to themesDir, not an absolute or `../` path.","If you need a local directory override, declare it in the PROJECT's config via `module.replacements` (or the HUGO_MODULE_REPLACEMENTS env var) — project-level paths are allowed to be absolute.","Move the local theme into the project's themes directory so a plain name resolves.","Run `hugo mod graph` to see which module declares the offending import."],"exampleFix":"# before (inside an imported theme's config)\n[[module.imports]]\npath = \"/home/dev/my-component\"\n# after (in the project's hugo.toml)\n[module]\nreplacements = \"github.com/org/my-component -> /home/dev/my-component\"","handlingStrategy":"validation","validationCode":"abs, _ := filepath.Abs(modPath)\nif !strings.HasPrefix(abs, projectDir) && !strings.HasPrefix(abs, themesDir) {\n    return fmt.Errorf(\"module path %q must live under themesDir when outside the project\", modPath)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep locally-referenced modules (replace directives, local paths) inside the project or under themesDir","Use relative paths in module imports rather than absolute paths outside the workspace","When using `replace` in go.mod for local development, point it inside themesDir"],"tags":["hugo-modules","path-traversal","configuration","themes"],"analyzedSha":"8a468df065a75c1c7cf9f6850f32148746590ea5","analyzedAt":"2026-07-31T21:23:07.045Z","schemaVersion":2}