gohugoio/hugo · error

failed to create config from modules config: %w

Error message

failed to create config from modules config: %w

What it means

After modules are resolved, if any module contributed its own config files (l.ModulesConfigFiles non-empty), Hugo re-runs fromLoadConfigResult so the module-supplied config is merged in, then checks configs.transientErr(). This error wraps a failure in that second decode pass or a deferred (transient) validation error, meaning the merged site+module config is invalid even though the site's own config alone was fine.

Source

Thrown at config/allconfig/load.go:86

		return nil, fmt.Errorf("failed to load config: %w", err)
	}

	configs, err = fromLoadConfigResult(d.Fs, d.Logger, res)
	if err != nil {
		return nil, fmt.Errorf("failed to create config from result: %w", err)
	}

	moduleConfig, modulesClient, err := l.loadModules(configs, d.IgnoreModuleDoesNotExist)
	if err != nil {
		return nil, fmt.Errorf("failed to load modules: %w", err)
	}

	if len(l.ModulesConfigFiles) > 0 {
		// Config merged in from modules.
		// Re-read the config.
		configs, err = fromLoadConfigResult(d.Fs, d.Logger, res)
		if err != nil {
			return nil, fmt.Errorf("failed to create config from modules config: %w", err)
		}
		if err := configs.transientErr(); err != nil {
			return nil, fmt.Errorf("failed to create config from modules config: %w", err)
		}
		configs.LoadingInfo.ConfigFiles = append(configs.LoadingInfo.ConfigFiles, l.ModulesConfigFiles...)
	} else if err := configs.transientErr(); err != nil {
		return nil, fmt.Errorf("failed to create config: %w", err)
	}

	configs.Modules = moduleConfig.AllModules
	configs.ModulesClient = modulesClient

	if !d.SkipNpmCheck && npm.NpmPackNeedsUpdate(d.Fs, configs.Modules) {
		d.Logger.Warnln(`npm dependencies are out of sync, please run "hugo mod npm pack" (you may also want to run "npm install" after that)`)
	}

	if err := configs.Init(d.Fs, d.Logger); err != nil {
		return nil, fmt.Errorf("failed to init config: %w", err)

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Inspect the wrapped error to see which section failed, then check the theme/module's own config file (in themes/<name>/ or the module cache) for that section.
  2. Align Hugo versions: check the theme's min_version and upgrade Hugo, or pin an older theme release.
  3. Override the offending key explicitly in your site config — site config wins over module config in the merge.
  4. Temporarily remove module imports one at a time to identify which module's config breaks the merge.
Defensive patterns

Strategy: try-catch

Validate before calling

// Check mount targets in module config are valid Hugo component folders
valid := map[string]bool{"content":true,"static":true,"layouts":true,"data":true,"assets":true,"i18n":true,"archetypes":true}
for _, m := range mounts {
    root := strings.Split(m.Target, "/")[0]
    if !valid[root] {
        return fmt.Errorf("invalid mount target %q", m.Target)
    }
}

Try / catch

conf, err := allconfig.LoadConfig(opts)
if err != nil {
    // modules resolved but their merged config failed to apply —
    // check imported themes'/modules' config files, not just your own
    return err
}

Prevention

When it happens

Trigger: A theme or module ships a hugo.toml/config.toml with sections that fail decoding, or whose values merged with the site config produce an invalid combination (e.g. module-provided output formats, params, or markup settings clashing with site settings); transientErr surfacing a deferred per-language validation failure after the merge.

Common situations: Upgrading a theme whose bundled config uses options from a newer Hugo than the installed binary; two modules contributing conflicting config for the same key; a site override that made sense standalone but is invalid once the theme's config merges under it.

Related errors


AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31). Data as JSON: /data/errors/2b4ee63cbb3d8bef.json. Report an issue: GitHub ↗.