gohugoio/hugo · error

invalid module config for %q: both source and target must be

Error message

invalid module config for %q: both source and target must be set

What it means

Returned by collector.normalizeMounts in modules/collect.go:800 when a module mount has an empty Source or Target. Mounts map a source path into Hugo's virtual filesystem; both ends are mandatory. The error names the offending module (owner.Path()).

Source

Thrown at modules/collect.go:800

		return s
	}
	if strings.HasPrefix(s, "../../node_modules/") {
		// See #14083. This was a common construct to mount node_modules from the project root.
		// This started failing in v0.152.0 when we tightened the validation.
		return strings.TrimPrefix(s, "../../")
	}
	return ""
}

func (c *collector) normalizeMounts(owner *moduleAdapter, mounts []Mount) ([]Mount, error) {
	var out []Mount
	dir := owner.Dir()

	for _, mnt := range mounts {
		errMsg := fmt.Sprintf("invalid module config for %q", owner.Path())

		if mnt.Source == "" || mnt.Target == "" {
			return nil, errors.New(errMsg + ": both source and target must be set")
		}

		// 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) {

View on GitHub (pinned to 52c9bd7908)

Solutions

  1. Find the mount flagged by the module name in the error and add the missing source and target.
  2. Ensure source is a non-empty path relative to the module dir and target is a Hugo filesystem dir (e.g. static, content, layouts, assets, archetypes, i18n, data).
  3. Run `hugo config` to print resolved mounts and spot the blank entry.

Example fix

// before
[[module.mounts]]
source = "content"
// after
[[module.mounts]]
source = "content"
target = "content"
Defensive patterns

Strategy: validation

Validate before calling

// Validate every mount has both fields before collection.
for _, m := range mounts {
    if m.Source == "" || m.Target == "" { return errors.New("mount missing source/target") }
}

Prevention

When it happens

Trigger: Declaring [[module.mounts]] with source or target omitted/blank, in either the project config or a theme/module's config. normalizeMounts runs during module collection for every mount of every active module.

Common situations: A typo'd [[module.mounts]] table missing a key, a config merge that dropped a field, or a theme upgrade whose mounts schema expects a field the project didn't supply.

Related errors


AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09). Data as JSON: /api/errors/df0f22abf300c538. Report an issue: GitHub.