gohugoio/hugo · error

value has type %s; should be %s

Error message

value has type %s; should be %s

What it means

Thrown by prepareArg during map indexing when the supplied index value is valid but its reflect.Type is not AssignableTo the map's key type. This is a hard type mismatch — e.g. indexing map[int]V with a string, or map[string]V with an int. Both the actual type and expected type are named via %s.

Source

Thrown at tpl/collections/index.go:131

			return nil, fmt.Errorf("can't index item of type %s", v.Type())
		}
	}
	return v.Interface(), nil
}

// prepareArg checks if value can be used as an argument of type argType, and
// converts an invalid value to appropriate zero if possible.
//
// Copied from Go stdlib src/text/template/funcs.go.
func prepareArg(value reflect.Value, argType reflect.Type) (reflect.Value, error) {
	if !value.IsValid() {
		if !canBeNil(argType) {
			return reflect.Value{}, fmt.Errorf("value is nil; should be of type %s", argType)
		}
		value = reflect.Zero(argType)
	}
	if !value.Type().AssignableTo(argType) {
		return reflect.Value{}, fmt.Errorf("value has type %s; should be %s", value.Type(), argType)
	}
	return value, nil
}

// canBeNil reports whether an untyped nil can be assigned to the type. See reflect.Zero.
//
// Copied from Go stdlib src/text/template/exec.go.
func canBeNil(typ reflect.Type) bool {
	switch typ.Kind() {
	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
		return true
	}
	return false
}

View on GitHub (pinned to 52c9bd7908)

Solutions

  1. Coerce the key to the map's key type: `{{ index $intMap (int $k) }}` or `{{ index $strMap (string $k) }}`.
  2. Align the map's declaration with the keys actually used.
  3. Standardize data ingestion so keys are consistently typed.
  4. Inspect both types in the error message to pick the correct coercion.

Example fix

// before
{{ index $intKeyedMap "5" }}
// after
{{ index $intKeyedMap (int "5") }}
Defensive patterns

Strategy: type-guard

Validate before calling

func assignableMapKey(mapV reflect.Value, key any) (reflect.Value, error) {
    kv := reflect.ValueOf(key)
    if !kv.IsValid() || !kv.Type().AssignableTo(mapV.Type().Key()) {
        return reflect.Value{}, fmt.Errorf("key %T not assignable to map key %s", key, mapV.Type().Key())
    }
    return kv, nil
}

Type guard

func keyMatchesMap(m, key any) bool {
    mv := reflect.ValueOf(m)
    if mv.Kind() != reflect.Map { return false }
    kv := reflect.ValueOf(key)
    if !kv.IsValid() { return false }
    return kv.Type().AssignableTo(mv.Type().Key())
}

Prevention

When it happens

Trigger: Calling `{{ index $intKeyedMap "1" }}` (string vs int key), `{{ index $stringKeyedMap 42 }}` (int vs string), or `{{ index $map $someStruct }}` where the struct isn't the key type. prepareArg's AssignableTo check fails.

Common situations: JSON/CSV data ingestion where keys arrive as strings but the map is int-keyed (or vice versa), template author guesses the key type, or a partial receives a key whose type differs across callers.

Related errors


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