gohugoio/hugo · error

can't index item of type %s

Error message

can't index item of type %s

What it means

Thrown inside doIndex when the item being indexed is reflect.Invalid-handled separately-or a kind that is neither Array, Slice, String, nor Map (e.g. struct, int, float, bool, func, chan). The item's reflect.Type is named via %s. This means the value itself is not indexable at all, regardless of the key supplied.

Source

Thrown at tpl/collections/index.go:113

				return nil, nil
			}
			v = v.Index(int(x))
		case reflect.Map:
			index, err := prepareArg(index, v.Type().Key())
			if err != nil {
				return nil, err
			}

			if x := v.MapIndex(index); x.IsValid() {
				v = x
			} else {
				v = reflect.Zero(v.Type().Elem())
			}
		case reflect.Invalid:
			// the loop holds invariant: v.IsValid()
			panic("unreachable")
		default:
			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)

View on GitHub (pinned to 52c9bd7908)

Solutions

  1. Use field/method access for structs: `{{ .Title }}` instead of `{{ index . "Title" }}`.
  2. Stop indexing once you reach a scalar leaf.
  3. Convert the value to a map if generic key access is required.
  4. Inspect the type named in the error to choose field vs index access.

Example fix

// before
{{ index .Page "Title" }}
// after
{{ .Page.Title }}
Defensive patterns

Strategy: type-guard

Validate before calling

func isIndexableKind(v any) bool {
    rv := reflect.ValueOf(v)
    if !rv.IsValid() { return false }
    switch rv.Kind() {
    case reflect.Slice, reflect.Array, reflect.String, reflect.Map:
        return true
    }
    return false
}

Type guard

func isIndexableCollection(v any) bool {
    return isIndexableKind(v)
}

Prevention

When it happens

Trigger: Calling `{{ index 42 0 }}`, `{{ index $pageObject "Title" }}` (page is a struct, not indexable — use `.Title` field access instead), `{{ index 3.14 "x" }}`, or `{{ index $func 0 }}`. The default branch in doIndex's switch over v.Kind() catches all non-indexable kinds.

Common situations: Author assumes an object is a map when it is a struct (pages, resources are structs), or chains index past a scalar leaf. Common when traversing nested data where a path segment resolves to a primitive but indexing continues.

Related errors


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