gohugoio/hugo · error

incompatible map types, got %T to %T

Error message

incompatible map types, got %T to %T

What it means

Both merge arguments are maps, but their reflect key types differ (e.g. map[string]any vs map[int]any), so Hugo cannot merge them coherently. The error prints both concrete Go types. Key comparison is on the map key type, not the element type.

Source

Thrown at tpl/collections/merge.go:65

// merge creates a copy of dst and merges src into it.
func (ns *Namespace) merge(src, dst any) (any, error) {
	vdst, vsrc := reflect.ValueOf(dst), reflect.ValueOf(src)

	if vdst.Kind() != reflect.Map {
		return nil, fmt.Errorf("destination must be a map, got %T", dst)
	}

	if !hreflect.IsTruthfulValue(vsrc) {
		return dst, nil
	}

	if vsrc.Kind() != reflect.Map {
		return nil, fmt.Errorf("source must be a map, got %T", src)
	}

	if vsrc.Type().Key() != vdst.Type().Key() {
		return nil, fmt.Errorf("incompatible map types, got %T to %T", src, dst)
	}

	return mergeMap(vdst, vsrc).Interface(), nil
}

func caseInsensitiveLookup(m, k reflect.Value) (reflect.Value, bool) {
	if m.Type().Key().Kind() != reflect.String || k.Kind() != reflect.String {
		// Fall back to direct lookup.
		v := m.MapIndex(k)
		return v, hreflect.IsTruthfulValue(v)
	}

	k2 := reflect.New(m.Type().Key()).Elem()

	iter := m.MapRange()
	for iter.Next() {
		k2.SetIterKey(iter)
		if strings.EqualFold(k.String(), k2.String()) {

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Print both types (`printf "%T / %T" $src $dst`) to identify the mismatched key type.
  2. Rebuild one side with string keys, e.g. reconstruct with `dict` in a loop, or quote numeric keys in the YAML/JSON source.
  3. Prefer `dict`-built maps and standard data files (which yield string keys) on both sides.
  4. Upgrade Hugo — newer versions normalize decoded maps to string keys more consistently.

Example fix

# before (data/nums.yaml)
1: one
2: two
# after
"1": one
"2": two
Defensive patterns

Strategy: validation

Validate before calling

{{/* ensure both maps use string keys */}}{{ $dst := dict "a" 1 }}{{ $src := dict "b" 2 }}{{ $merged := merge $dst $src }}

Type guard

{{ $compatible := and (reflect.IsMap $dst) (reflect.IsMap $src) }}

Try / catch

{{ with try (merge $dst $src) }}{{ with .Err }}{{ errorf "incompatible map types: %s" . }}{{ end }}{{ end }}

Prevention

When it happens

Trigger: `merge` called with maps whose key types differ: a `map[string]interface{}` from front matter merged with a `map[interface{}]interface{}` from an older YAML decoder or a Go-constructed `map[int]...` from a data source. Constructing a dict with non-string keys via custom shortcode/partial data.

Common situations: Mixing data from JSON (string keys) with YAML sources decoded to interface keys in older Hugo/library versions; merging site config maps with maps built by `dict` (always string-keyed) against maps from external data files with numeric keys; version upgrades changing the YAML library's map key type.

Related errors


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