gohugoio/hugo · error
source must be a map, got %T
Error message
source must be a map, got %T
What it means
Thrown by Namespace.merge when a source parameter (any non-final param to Merge) is reflect-truthy but not reflect.Map. Sources contribute keys to the destination; a non-map source cannot be merged. The %T verb names the offending source type. Note a nil/zero source is silently skipped (IsTruthfulValue guard), so this fires only for truthy non-maps.
Solutions
- Pass only maps as sources: `{{ merge $overrides $defaults $dst }}`.
- Convert a struct to a map before merging (e.g. extract needed fields into a dict).
- Drop sources that are unexpectedly non-map.
- Validate each source is a map before the call.
Example fix
// before
{{ merge $pageList $dstMap }}
// after
{{ merge (dict "key" "val") $dstMap }} Defensive patterns
Strategy: type-guard
Validate before calling
func mergeSourcesAreMaps(params ...any) error {
for i := 0; i < len(params)-1; i++ {
if !hreflect.IsTruthfulValue(reflect.ValueOf(params[i])) { continue }
if reflect.ValueOf(params[i]).Kind() != reflect.Map {
return fmt.Errorf("merge source must be map, got %T", params[i])
}
}
return nil
} Type guard
func isMap(v any) bool {
if v == nil { return false }
return reflect.ValueOf(v).Kind() == reflect.Map
} Prevention
- Pass only maps as Merge sources.
- Convert structs/lists to dicts before merging.
- Remember truthy non-maps are rejected (nil sources are tolerated).
- Validate each source is a map at the call site.
When it happens
Trigger: Calling `{{ merge $notAMap $dstMap }}` where $notAMap is a truthy slice, struct, or scalar. Because Merge iterates sources in reverse, each preceding param is validated here.
Common situations: Author passes a list or page object intending its fields to merge, or a partial receives heterogeneous args. A nil source is tolerated, but any truthy non-map triggers this.
Related errors
- destination 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/b13a8f025ebea695.
Report an issue: GitHub.
Appendix: source
Thrown at tpl/collections/merge.go:61
}
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) {
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()
View on GitHub (pinned to 52c9bd7908)