gohugoio/hugo · error
%s isn't a key of map type %s
Error message
%s isn't a key of map type %s
What it means
The element is a map, but the string key from the `where` path is not assignable to the map's key type (e.g. the map is keyed by int or another non-string type). Note this is a key-type mismatch, not a missing key — a missing string key on a string-keyed map simply yields a zero value.
Source
Thrown at tpl/collections/where.go:382
return zero, fmt.Errorf("can't evaluate a nil pointer of type %s by a struct field or map key name %s", typ, elemName)
}
obj = reflect.Indirect(obj)
switch obj.Kind() {
case reflect.Struct:
ft, ok := obj.Type().FieldByName(elemName)
if ok {
if ft.PkgPath != "" && !ft.Anonymous {
return zero, fmt.Errorf("%s is an unexported field of struct type %s", elemName, typ)
}
return obj.FieldByIndex(ft.Index), nil
}
return zero, fmt.Errorf("%s isn't a field of struct type %s", elemName, typ)
case reflect.Map:
kv := reflect.ValueOf(elemName)
if kv.Type().AssignableTo(obj.Type().Key()) {
return obj.MapIndex(kv), nil
}
return zero, fmt.Errorf("%s isn't a key of map type %s", elemName, typ)
}
return zero, fmt.Errorf("%s is neither a struct field, a method nor a map element of type %s", elemName, typ)
}
// parseWhereArgs parses the end arguments to the where function. Return a
// match value and an operator, if one is defined.
func parseWhereArgs(args ...any) (mv reflect.Value, op string, err error) {
switch len(args) {
case 1:
mv = reflect.ValueOf(args[0])
case 2:
var ok bool
if op, ok = args[0].(string); !ok {
err = errors.New("operator argument must be string type")
return
}
op = strings.TrimSpace(strings.ToLower(op))
mv = reflect.ValueOf(args[1])View on GitHub ↗ (pinned to 8a468df065)
Solutions
- Restructure the data so maps are keyed by strings (quote numeric keys in YAML: `"2024":`).
- Convert the collection into a slice of string-keyed maps before filtering.
- If filtering by value rather than sub-key, iterate with `range` and compare manually.
Example fix
# before (data/years.yaml -> map[int]...) 2024: title: A # after "2024": title: A
Defensive patterns
Strategy: type-guard
Validate before calling
{{ $first := index $dicts 0 }}
{{ if isset $first "category" }}{{ $r = where $dicts "category" "news" }}{{ end }} Type guard
{{/* guard: key present in map element */}}
{{ define "hasKey" }}{{ return isset (index . 0) (index . 1) }}{{ end }} Prevention
- Check the key with isset on a sample element before filtering
- Normalize data (e.g. in data files or front matter) so every map has the same keys
- Mind key case: map keys are matched exactly for non-string-insensitive maps
When it happens
Trigger: `where` over a collection of maps whose key type isn't string (e.g. `map[int]any` built via template logic or data with integer keys), so `reflect.ValueOf(elemName)` (a string) isn't AssignableTo the map's key type.
Common situations: YAML/TOML data that produced integer-keyed maps (e.g. `2024: ...` year keys) then filtered with a string path; programmatically built dicts with non-string keys passed to `where`.
Related errors
- %s is neither a struct field, a method nor a map element of
- arguments to complement must be slices or arrays
- can't iterate over %T
- invalid intersect values
- no such operator
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/ced83b143fe00a65.json.
Report an issue: GitHub ↗.