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

  1. Restructure the data so maps are keyed by strings (quote numeric keys in YAML: `"2024":`).
  2. Convert the collection into a slice of string-keyed maps before filtering.
  3. 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

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


AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31). Data as JSON: /data/errors/ced83b143fe00a65.json. Report an issue: GitHub ↗.