gohugoio/hugo · error

can't iterate over %T

Error message

can't iterate over %T

What it means

Thrown by Namespace.Where when the collection `c` is reflect-kind neither Array, Slice, nor Map. Where filters a collection by a key/field and operator; it supports slices/arrays (element-wise field match) and maps (value-wise). Any other kind (struct, string, int, func, chan) is rejected with the collection's type named via %T.

Source

Thrown at tpl/collections/where.go:55

	if err != nil {
		return nil, err
	}

	ctxv := reflect.ValueOf(ctx)

	var path []string
	kv := reflect.ValueOf(key)
	if kv.Kind() == reflect.String {
		path = strings.Split(strings.Trim(kv.String(), "."), ".")
	}

	switch seqv.Kind() {
	case reflect.Array, reflect.Slice:
		return ns.checkWhereArray(ctxv, seqv, kv, mv, path, op)
	case reflect.Map:
		return ns.checkWhereMap(ctxv, seqv, kv, mv, path, op)
	default:
		return nil, fmt.Errorf("can't iterate over %T", c)
	}
}

func (ns *Namespace) checkCondition(v, mv reflect.Value, op string) (bool, error) {
	v, vIsNil := hreflect.Indirect(v)
	if !v.IsValid() {
		vIsNil = true
	}

	mv, mvIsNil := hreflect.Indirect(mv)
	if !mv.IsValid() {
		mvIsNil = true
	}
	if vIsNil || mvIsNil {
		switch op {
		case "", "=", "==", "eq":
			return vIsNil == mvIsNil, nil
		case "!=", "<>", "ne":

View on GitHub (pinned to 52c9bd7908)

Solutions

  1. Pass a slice/map of items: `{{ where .Pages "Section" "=" "posts" }}`.
  2. Wrap a single item: `{{ where (slice .Page) "Section" "=" "posts" }}`.
  3. Verify the variable is a collection before calling where.
  4. Use a different filter (e.g. `if eq .Section "posts"`) for single values.

Example fix

// before
{{ where .Page "Section" "=" "posts" }}
// after
{{ where (slice .Page) "Section" "=" "posts" }}
Defensive patterns

Strategy: type-guard

Validate before calling

func whereCollectionValid(c any) error {
    k := reflect.ValueOf(c).Kind()
    if k != reflect.Slice && k != reflect.Array && k != reflect.Map {
        return fmt.Errorf("where needs slice/array/map, got %T", c)
    }
    return nil
}

Type guard

func isWhereable(v any) bool {
    if v == nil { return false }
    k := reflect.ValueOf(v).Kind()
    return k == reflect.Slice || k == reflect.Array || k == reflect.Map
}

Prevention

When it happens

Trigger: Calling `{{ where $singlePage "Section" "=" "posts" }}` (a struct, not a collection), `{{ where "hello" 0 "=" "h" }}`, or `{{ where 42 "k" "=" 1 }}`. The switch over seqv.Kind() handles only Slice/Array/Map; everything else hits the default branch.

Common situations: Author forgets to wrap a single page in a slice, passes a scalar variable that was expected to be a list, or chains where after an operation that collapsed a slice to a single value. Common with `.Page` vs `.Pages` mix-ups.

Related errors


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