gohugoio/hugo · error

expected a slice, got %T

Error message

expected a slice, got %T

What it means

Returned by collections.Append when the `to` argument is not nil and does not resolve (after dereferencing pointers/interfaces) to a reflect.Slice kind. Append's contract is 'append one slice to another'; a non-slice receiver breaks that contract. The %T verb substitutes the actual Go type of the offending value.

Solutions

  1. Make sure the first argument is a slice (e.g. []string, []any) or nil/nil slice.
  2. If the value may be a single element, wrap it: `[]any{value}` before appending.
  3. If loading from config/frontmatter, validate the field is a list before calling Append.
  4. For arrays, convert to a slice first via `s[:]`.

Example fix

// before
collections.Append("hello", "world")
// after
collections.Append([]string{"hello"}, "world")
Defensive patterns

Strategy: type-guard

Validate before calling

func isSlice(v any) bool {
  if v == nil { return true }
  rv := reflect.ValueOf(v)
  for rv.Kind() == reflect.Pointer || rv.Kind() == reflect.Interface {
    if rv.IsNil() { return true }
    rv = rv.Elem()
  }
  return rv.Kind() == reflect.Slice
}

Type guard

func isAppendableSlice(v any) bool { return isSlice(v) }

Prevention

When it happens

Trigger: Calling collections.Append(to, from...) where `to` is a struct, map, scalar, array ([N]T not []T), or a pointer to a non-slice. Pass nil to `to` is allowed; passing a concrete non-slice value is not.

Common situations: Template authors calling `append` (Hugo's `collections.Append` exposed as `append`) with the head argument bound to `.Params.someScalar` or a map instead of a slice. Programmatic callers passing `&myStruct` by mistake instead of `&mySlice`.

Related errors


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

Appendix: 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 52c9bd7908)