{"record":{"id":"134cf20b1d295f3a","repo":"gohugoio/hugo","slug":"destination-must-be-a-map-got-t","errorCode":null,"errorMessage":"destination must be a map, got %T","messagePattern":"destination must be a map, got %T","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tpl/collections/merge.go","lineNumber":53,"sourceCode":"\tvar err error\n\tresult := params[len(params)-1]\n\n\tfor i := len(params) - 2; i >= 0; i-- {\n\t\tresult, err = ns.merge(params[i], result)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn result, nil\n}\n\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) {","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/gohugoio/hugo/blob/52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2/tpl/collections/merge.go#L35-L71","documentation":"Thrown by Namespace.merge (helper for Merge) when the destination map — the LAST parameter to Merge — is not reflect.Map. Merge copies the destination and merges preceding sources into it; the destination must be a map to receive keys. The %T verb names the offending destination type.","triggerScenarios":"Calling `{{ merge $src $notAMap }}` where $notAMap is a slice, struct, string, or scalar. Note Merge treats the final param as destination and iterates backward, so this fires when params[len(params)-1] is non-map.","commonSituations":"Argument order confusion (author assumes first arg is destination), passing a page or resource struct as the merge target, or a variable that resolved to nil/non-map after data loading.","solutions":["Ensure the last argument is a map: `{{ merge $defaults $page.Params }}` (destination last).","Construct a fresh map destination: `{{ merge $src (dict \"k\" \"v\") }}`.","Re-read Merge docs: result is a copy of the final param with earlier params merged in reverse.","Log the type of each argument to verify ordering."],"exampleFix":"// before\n{{ merge $page.Params $defaults }}\n// after\n{{ merge $defaults $page.Params }}","handlingStrategy":"type-guard","validationCode":"func mergeDstIsMap(params ...any) error {\n    if len(params) < 2 { return errors.New(\"need >=2 params\") }\n    dst := params[len(params)-1]\n    if reflect.ValueOf(dst).Kind() != reflect.Map {\n        return fmt.Errorf(\"merge destination must be map, got %T\", dst)\n    }\n    return nil\n}","typeGuard":"func isMap(v any) bool {\n    if v == nil { return false }\n    return reflect.ValueOf(v).Kind() == reflect.Map\n}","tryCatchPattern":null,"preventionTips":["Put the destination map LAST in Merge calls.","Construct a fresh dict as destination when in doubt.","Remember Merge returns a copy of the final param with sources merged in.","Log each Merge argument's type to verify ordering."],"tags":["go","hugo","template","collections","merge","type-error"],"backgroundTag":null,"analyzedSha":"52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2","analyzedAt":"2026-08-09T21:49:36.660Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}