gohugoio/hugo · error

failed to unmarshal config for path %q: %w

Error message

failed to unmarshal config for path %q: %w

What it means

Raised while loading the split config directory (`config/_default`, `config/production`, etc.): a config file found during the walk failed to parse via metadecoders.UnmarshalFileToMap. The error wraps the underlying TOML/YAML/JSON syntax error and pins error reporting to the exact file path.

Source

Thrown at config/configLoader.go:172

				return nil
			}

			if fi.IsDir() {
				dirnames = append(dirnames, path)
				return nil
			}

			if !IsValidConfigFilename(path) {
				return nil
			}

			name := paths.Filename(filepath.Base(path))

			item, err := metadecoders.Default.UnmarshalFileToMap(sourceFs, path)
			if err != nil {
				// This will be used in error reporting, use the most specific value.
				dirnames = []string{path}
				return fmt.Errorf("failed to unmarshal config for path %q: %w", path, err)
			}

			var keyPath []string
			var unwrapKey string
			if !DefaultConfigNamesSet[name] {
				// Can be params.jp, menus.en etc.
				name, lang := paths.FileAndExtNoDelimiter(name)
				unwrapKey = name

				keyPath = []string{name}

				if lang != "" {
					keyPath = []string{"languages", lang}
					switch name {
					case "menu", "menus":
						keyPath = append(keyPath, "menus")
					case "params":
						keyPath = append(keyPath, "params")

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Open the file at the quoted path and fix the syntax error described in the wrapped message (it includes line/position for TOML/YAML).
  2. Ensure the file's content matches its extension — TOML in `.toml`, YAML in `.yaml`.
  3. Check for leftover git conflict markers or editor artifacts; validate the file with a TOML/YAML linter.
  4. Move non-config files out of the config directory.

Example fix

# before (config/_default/params.toml)
[social
twitter = "user"

# after
[social]
twitter = "user"
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check that the value at the path is a table/map before decoding into a struct
v := cfg.Get("outputFormats")
if v != nil {
    if _, ok := v.(maps.Params); !ok {
        return fmt.Errorf("config key %q must be a table, got %T", "outputFormats", v)
    }
}

Type guard

func asParams(v any) (maps.Params, bool) {
    p, ok := v.(maps.Params)
    return p, ok
}

Try / catch

conf, err := allconfig.LoadConfig(configs.ConfigSourceDescriptor{...})
if err != nil {
    // "failed to unmarshal config for path %q" names the offending top-level key —
    // its TOML/YAML shape (scalar vs table vs array) doesn't match the expected schema
    return fmt.Errorf("load config: %w", err)
}

Prevention

When it happens

Trigger: Any file with a valid config filename/extension under a config dir containing malformed content — TOML with unbalanced brackets or duplicate keys, YAML with bad indentation or tabs, JSON with trailing commas — encountered by `hugo`, `hugo server`, or `hugo config` when merging the config directory.

Common situations: Merge-conflict markers (`<<<<<<<`) left in a config file; editing `config/_default/params.toml` with YAML syntax (or vice versa) so the extension doesn't match the content; smart quotes pasted from docs; stray non-config text files saved with a `.toml`/`.yaml` extension in the config tree.

Related errors


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