gohugoio/hugo · error

expected a slice, got %T

Error message

expected a slice, got %T

What it means

`collections.Append` implements the template `append` function. After dereferencing the first argument, if it's non-nil but not a slice (a string, map, number, page, etc.), appending is impossible and this error names the concrete type. Only slices (or nil, which starts a new slice) are valid append targets.

Source

Thrown at common/collections/append.go:44

func Append(to any, from ...any) (any, error) {
	if len(from) == 0 {
		return to, nil
	}
	tov, toIsNil := hreflect.Indirect(reflect.ValueOf(to))

	toIsNil = toIsNil || to == nil
	var tot reflect.Type

	if !toIsNil {
		if tov.Kind() == reflect.Slice {
			// Create a copy of tov, so we don't modify the original.
			c := reflect.MakeSlice(tov.Type(), tov.Len(), tov.Len()+len(from))
			reflect.Copy(c, tov)
			tov = c
		}

		if tov.Kind() != reflect.Slice {
			return nil, fmt.Errorf("expected a slice, got %T", to)
		}

		tot = tov.Type().Elem()
		if tot.Kind() == reflect.Slice {
			totvt := tot.Elem()
			fromvs := make([]reflect.Value, len(from))
			for i, f := range from {
				fromv := reflect.ValueOf(f)
				fromt := fromv.Type()
				if fromt.Kind() == reflect.Slice {
					fromt = fromt.Elem()
				}
				if totvt != fromt {
					return nil, fmt.Errorf("cannot append slice of %s to slice of %s", fromt, totvt)
				} else {
					fromvs[i] = fromv
				}
			}

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Ensure the collection argument (the last one in Hugo's `append`) is a slice — initialize with `$s := slice`, not a string.
  2. If appending to a page param, fix the front matter so the field is a list (`items: [a]` not `items: a`).
  3. When accumulating in templates, start with `$acc := slice` and reassign: `$acc = $acc | append $item`.

Example fix

{{/* before */}}
{{ $s := "" }}
{{ $s = append "x" $s }}

{{/* after */}}
{{ $s := slice }}
{{ $s = append "x" $s }}
Defensive patterns

Strategy: type-guard

Validate before calling

rv := reflect.ValueOf(to)
if rv.Kind() != reflect.Slice && rv.Kind() != reflect.Array {
    return fmt.Errorf("append target must be a slice, got %T", to)
}

Type guard

func isSlice(v any) bool {
    k := reflect.TypeOf(v).Kind()
    return k == reflect.Slice || k == reflect.Array
}

Try / catch

{{ $s := $x }}
{{ if not (reflect.IsSlice $s) }}{{ $s = slice $s }}{{ end }}
{{ $s = $s | append $item }}

Prevention

When it happens

Trigger: Template code calling `append` with a non-slice first collection: `{{ append "a" $x }}`, `{{ append .Params.title $x }}`, or appending onto a value produced by `dict`. Note the argument order — in Hugo, `append ELEMENT COLLECTION` (collection last) also flows through this when the collection position holds a non-slice.

Common situations: Confusing Hugo's `append` argument order with Go's, so a scalar ends up in the slice position; a `.Params` field expected to be a list but authored as a string in front matter; appending to a `.Scratch` value that was set to a scalar earlier.

Related errors


AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31). Data as JSON: /data/errors/5fd1561695aef9d5.json. Report an issue: GitHub ↗.