gohugoio/hugo · error
isset unable to use key of type %T as index
Error message
isset unable to use key of type %T as index
What it means
Thrown by Hugo's `isset` template function (Namespace.IsSet) when the collection argument is an array, channel, or slice but the supplied key cannot be converted to an integer index via cast.ToIntE. Arrays/slices/channels are positional, so a numeric index is mandatory; any other key type (string, struct, map, bool) is meaningless as a positional lookup and is rejected. The %T verb fills in the actual Go type of the offending key.
Solutions
- Pass an integer literal or integer-typed value as the key, e.g. `{{ isset $slice 0 }}`.
- If you meant string-key lookup, ensure the first argument is a map (`map[string]...`), not a slice/array.
- If the key is dynamic, coerce it to int before the call: `{{ $k := int $key }}{{ isset $slice $k }}`.
- Guard the call with a type check or restructure data so the same collection is always addressed by index.
Example fix
// before
{{ if isset .Pages "title" }}...{{ end }}
// after
{{ if isset .Pages 0 }}...{{ end }}
// or, if the collection is actually a map:
{{ if isset .Params "title" }}...{{ end }} Defensive patterns
Strategy: validation
Validate before calling
// Validate key is int before IsSet on an array/slice/chan.
func isSetIntKey(c, key any) bool {
v := reflect.ValueOf(c)
if k := v.Kind(); k == reflect.Array || k == reflect.Slice || k == reflect.Chan {
if n, err := cast.ToIntE(key); err == nil {
return v.Len() > n
}
return false // would trigger the error; bail out
}
// fall through to map handling
return false
} Type guard
func isIndexableByKey(c any) bool {
v := reflect.ValueOf(c)
switch v.Kind() {
case reflect.Array, reflect.Slice, reflect.Chan:
return true
case reflect.Map:
return true
}
return false
} Prevention
- Always pass an integer literal or int-typed variable when calling isset on a slice/array/chan.
- Distinguish map-keyed collections (.Params) from positional ones (.Pages) at the template level.
- If the key is dynamic, coerce it with int() before isset.
- Document collection shapes in partial signatures so callers pass correct key types.
When it happens
Trigger: Calling `{{ isset $slice "foo" }}`, `{{ isset .Pages "title" }}`, or `{{ isset $array $someStruct }}` — i.e. passing a non-integer second argument where the first is an array/slice/chan. Also triggers when the key is a float like 1.5 (cast.ToIntE rejects it) or a numeric string that cast cannot coerce.
Common situations: Template author confuses a slice with a map (e.g. treats `.Pages` as key-addressable by string), passes front-matter string keys to a positional collection, or flows an `interface{}` whose dynamic type is a string into `isset` against a slice. Common during partial refactors where a map was replaced by a slice but template calls were not updated.
Related errors
- arguments must be slices or arrays
- arguments to complement must be slices or arrays
- arguments to symdiff must be slices or arrays
- can't index item of type
- can't iterate over %T
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/631a3770cdeefea2.
Report an issue: GitHub.
Appendix: source
Thrown at tpl/collections/collections.go:340
if g, ok := in.(collections.Grouper); ok {
return g.Group(key, items)
}
return nil, fmt.Errorf("grouping not supported for type %T %T", items, in)
}
// IsSet returns whether a given array, channel, slice, or map in c has the given key
// defined.
func (ns *Namespace) IsSet(c any, key any) (bool, error) {
av := reflect.ValueOf(c)
kv := reflect.ValueOf(key)
switch av.Kind() {
case reflect.Array, reflect.Chan, reflect.Slice:
k, err := cast.ToIntE(key)
if err != nil {
return false, fmt.Errorf("isset unable to use key of type %T as index", key)
}
if av.Len() > k {
return true, nil
}
case reflect.Map:
if kv.Type() == av.Type().Key() {
return av.MapIndex(kv).IsValid(), nil
}
default:
ns.deps.Log.Warnf("calling IsSet with unsupported type %q (%T) for key %v will always return false.\n", av.Kind(), c, key)
}
return false, nil
}
// Last returns the last limit items in the list l.
func (ns *Namespace) Last(limit any, l any) (any, error) {
if limit == nil || l == nil {View on GitHub (pinned to 52c9bd7908)