gohugoio/hugo · error

isn't a key of map type

Error message

%s isn't a key of map type %s

What it means

In the map branch of `evaluateSubElem` (where.go:377-382), Hugo builds a reflect.Value of the element name and tests `kv.Type().AssignableTo(obj.Type().Key())`. If the name's type is not assignable to the map's key type, the lookup cannot proceed and Hugo reports that the name is not a key of that map type. Note the name is always a string here, so this commonly means a non-string-keyed map.

Solutions

  1. If you need integer keys, restructure the data into a slice of structs with string fields before templating.
  2. Convert the map to `map[string]T` during data preparation.
  3. Use a different filtering strategy (range + index) instead of `where` on a non-string-keyed map.

Example fix

// before
{{ where $m "42" "==" v }}  {{/* map[int]string */}}
// after
{{/* pre-convert to map[string]string or a slice of structs */}}
{{ where $items "ID" "==" "42" }}
Defensive patterns

Strategy: validation

Validate before calling

// Go-side: ensure maps used with `where` have string keys
if t.Kind() == reflect.Map && t.Key().Kind() != reflect.String {
    // convert to map[string]T or a slice of structs
}

Prevention

When it happens

Trigger: `{{ where $m "42" ... }}` against `map[int]string`; any map whose key type is not `string`, since the where key path is always string-derived.

Common situations: Filtering a `map[int]T` or `map[customType]T` by a string key from a template; assuming map keys are strings when they are integers.

Related errors


AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09). Data as JSON: /api/errors/ced83b143fe00a65. Report an issue: GitHub.

Appendix: 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 52c9bd7908)