gohugoio/hugo · error
destination must be a map, got %T
Error message
destination must be a map, got %T
What it means
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.
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.
Example fix
// before
{{ merge $page.Params $defaults }}
// after
{{ merge $defaults $page.Params }} Defensive patterns
Strategy: type-guard
Validate before calling
func mergeDstIsMap(params ...any) error {
if len(params) < 2 { return errors.New("need >=2 params") }
dst := params[len(params)-1]
if reflect.ValueOf(dst).Kind() != reflect.Map {
return fmt.Errorf("merge destination must be map, got %T", dst)
}
return nil
} Type guard
func isMap(v any) bool {
if v == nil { return false }
return reflect.ValueOf(v).Kind() == reflect.Map
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- source must be a map, got %T
- arguments must be slices or arrays
- arguments to complement must be slices or arrays
- arguments to symdiff must be slices or arrays
- can't index item of type
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/134cf20b1d295f3a.
Report an issue: GitHub.
Appendix: source
Thrown at tpl/collections/merge.go:53
var err error
result := params[len(params)-1]
for i := len(params) - 2; i >= 0; i-- {
result, err = ns.merge(params[i], result)
if err != nil {
return nil, err
}
}
return result, nil
}
// 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) {View on GitHub (pinned to 52c9bd7908)