gohugoio/hugo · error
%s: %q: mount source must be a local path for modules/themes
Error message
%s: %q: mount source must be a local path for modules/themes
What it means
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.
Source
Thrown at modules/collect.go:819
}
// Special case for node_modules imports in themes/modules.
// See #14089.
var isModuleNodeModulesImport bool
if !owner.projectMod {
nodeModulesImportSource := c.nodeModulesRoot(mnt.Source)
if nodeModulesImportSource != "" {
isModuleNodeModulesImport = true
mnt.Source = nodeModulesImportSource
}
}
mnt.Source = filepath.Clean(mnt.Source)
mnt.Target = filepath.Clean(mnt.Target)
var sourceDir string
if !owner.projectMod && !filepath.IsLocal(mnt.Source) {
return nil, fmt.Errorf("%s: %q: mount source must be a local path for modules/themes", errMsg, mnt.Source)
}
if filepath.IsAbs(mnt.Source) {
// Abs paths in the main project is allowed.
sourceDir = mnt.Source
} else {
sourceDir = filepath.Join(dir, mnt.Source)
}
// Verify that Source exists
_, err := c.fs.Stat(sourceDir)
if err != nil {
if paths.IsSameFilePath(sourceDir, c.ccfg.PublishDir) {
// This is a little exotic, but there are use cases for mounting the public folder.
// This will typically also be in .gitingore, so create it.
if err := c.fs.MkdirAll(sourceDir, 0o755); err != nil {
return nil, fmt.Errorf("%s: %q", errMsg, err)
}
} else if strings.HasSuffix(sourceDir, files.FilenameHugoStatsJSON) {View on GitHub ↗ (pinned to 8a468df065)
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.
Example fix
# theme/hugo.toml — before [[module.mounts]] source = "../shared/assets" target = "assets" # after (declare in the project's hugo.toml instead) [[module.mounts]] source = "/abs/or/relative/shared/assets" target = "assets"
Defensive patterns
Strategy: validation
Validate before calling
// before calling modules.Collect / hugo build
for _, m := range cfg.Mounts {
if strings.HasPrefix(m.Source, "/") || filepath.IsAbs(m.Source) || strings.Contains(m.Source, "://") {
return fmt.Errorf("mount source %q must be a relative local path", m.Source)
}
} Prevention
- 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
When it happens
Trigger: 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`).
Common situations: 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.
Related errors
- %s: mount target must be one of: %v
- failed to init mount %q %d: %w
- failed to init mount %d: %w
- failed to apply mounts for project: %w
- language %q not found
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/2cb025081b61fd50.json.
Report an issue: GitHub ↗.