{"id":"6329c41db7f1af5e","repo":"gohugoio/hugo","slug":"incompatible-map-types-got-t-to-t","errorCode":null,"errorMessage":"incompatible map types, got %T to %T","messagePattern":"incompatible map types, got %T to %T","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tpl/collections/merge.go","lineNumber":65,"sourceCode":"\n// merge creates a copy of dst and merges src into it.\nfunc (ns *Namespace) merge(src, dst any) (any, error) {\n\tvdst, vsrc := reflect.ValueOf(dst), reflect.ValueOf(src)\n\n\tif vdst.Kind() != reflect.Map {\n\t\treturn nil, fmt.Errorf(\"destination must be a map, got %T\", dst)\n\t}\n\n\tif !hreflect.IsTruthfulValue(vsrc) {\n\t\treturn dst, nil\n\t}\n\n\tif vsrc.Kind() != reflect.Map {\n\t\treturn nil, fmt.Errorf(\"source must be a map, got %T\", src)\n\t}\n\n\tif vsrc.Type().Key() != vdst.Type().Key() {\n\t\treturn nil, fmt.Errorf(\"incompatible map types, got %T to %T\", src, dst)\n\t}\n\n\treturn mergeMap(vdst, vsrc).Interface(), nil\n}\n\nfunc caseInsensitiveLookup(m, k reflect.Value) (reflect.Value, bool) {\n\tif m.Type().Key().Kind() != reflect.String || k.Kind() != reflect.String {\n\t\t// Fall back to direct lookup.\n\t\tv := m.MapIndex(k)\n\t\treturn v, hreflect.IsTruthfulValue(v)\n\t}\n\n\tk2 := reflect.New(m.Type().Key()).Elem()\n\n\titer := m.MapRange()\n\tfor iter.Next() {\n\t\tk2.SetIterKey(iter)\n\t\tif strings.EqualFold(k.String(), k2.String()) {","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/gohugoio/hugo/blob/8a468df065a75c1c7cf9f6850f32148746590ea5/tpl/collections/merge.go#L47-L83","documentation":"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.","triggerScenarios":"`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.","commonSituations":"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.","solutions":["Print both types (`printf \"%T / %T\" $src $dst`) to identify the mismatched key type.","Rebuild one side with string keys, e.g. reconstruct with `dict` in a loop, or quote numeric keys in the YAML/JSON source.","Prefer `dict`-built maps and standard data files (which yield string keys) on both sides.","Upgrade Hugo — newer versions normalize decoded maps to string keys more consistently."],"exampleFix":"# before (data/nums.yaml)\n1: one\n2: two\n# after\n\"1\": one\n\"2\": two","handlingStrategy":"validation","validationCode":"{{/* ensure both maps use string keys */}}{{ $dst := dict \"a\" 1 }}{{ $src := dict \"b\" 2 }}{{ $merged := merge $dst $src }}","typeGuard":"{{ $compatible := and (reflect.IsMap $dst) (reflect.IsMap $src) }}","tryCatchPattern":"{{ with try (merge $dst $src) }}{{ with .Err }}{{ errorf \"incompatible map types: %s\" . }}{{ end }}{{ end }}","preventionTips":["Build both maps with dict so key types match (string keys)","Avoid merging maps sourced from differently-typed data files (e.g. JSON vs TOML with integer keys)","Normalize data through jsonify/unmarshal round-trip if key types may differ"],"tags":["hugo","templates","merge","map-keys"],"analyzedSha":"8a468df065a75c1c7cf9f6850f32148746590ea5","analyzedAt":"2026-07-31T21:23:07.045Z","schemaVersion":2}