gohugoio/hugo · error
cannot append slice of %s to slice of %s
Error message
cannot append slice of %s to slice of %s
What it means
Hugo's collections.Append (backing the `append` template function) uses reflection to append values to a typed slice. When the target is a slice of slices (e.g. [][]string), every appended element must be a slice with the same element type; if the incoming value's element type differs from the target's, Append fails with this error at common/collections/append.go:58 rather than silently producing a mixed-type slice.
Source
Thrown at common/collections/append.go:58
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
}
}
return reflect.Append(tov, fromvs...).Interface(), nil
}
toIsNil = tov.Len() == 0
if len(from) == 1 {
fromv := reflect.ValueOf(from[0])
if !fromv.IsValid() {
// from[0] is nil
return appendToInterfaceSliceFromValues(tov, fromv)
}
fromt := fromv.Type()
if fromt.Kind() == reflect.Slice {View on GitHub ↗ (pinned to 8a468df065)
Solutions
- Make the appended slice's element type match the target, e.g. convert values with `string`/`int` before building the inner slice.
- Seed the outer slice as []interface{} (e.g. start with `slice` of mixed values) so any inner slice type is accepted.
- Check where the target slice was first created and make all rows come from the same source/type.
Example fix
{{/* before */}}
{{ $rows := slice (slice "a" "b") }}
{{ $rows = $rows | append (slice 1 2) }} {{/* error */}}
{{/* after */}}
{{ $rows := slice (slice "a" "b") }}
{{ $rows = $rows | append (slice "1" "2") }} Defensive patterns
Strategy: type-guard
Validate before calling
// Go caller: ensure element types match before append
if reflect.TypeOf(elem) != reflect.TypeOf(slice).Elem() { /* wrap in []interface{} or convert first */ } Type guard
func canAppend(slice, elem any) bool {
sv := reflect.ValueOf(slice)
if sv.Kind() != reflect.Slice { return false }
et := sv.Type().Elem()
return et.Kind() == reflect.Interface || reflect.TypeOf(elem).AssignableTo(et)
} Try / catch
res, err := collections.Append(to, from...)
if err != nil {
// mixed element types: rebuild target as []interface{} and retry once
} Prevention
- Keep slices homogeneous, or start from []interface{} when mixing types (in templates: `slice` builds []interface{}).
- When appending one typed slice to another, convert elements to the destination element type first.
- In templates, avoid appending a []string to []int etc.; normalize with `slice` before `append`.
When it happens
Trigger: Calling `{{ $s = $s | append ... }}` in a template where $s is a slice of slices (e.g. built from `slice (slice "a")`) and the appended value is a slice of a different element type, such as appending (slice 1 2) to [][]string, or appending a []interface{} to a [][]string.
Common situations: Templates that accumulate rows/groups (e.g. building a matrix of strings) where one appended row comes from a differently-typed source like .Params (which yields []interface{}) or integers mixed with strings; refactors that change the initial seed slice's type; data files decoding to different concrete types than the literal `slice` function produces.
Related errors
- arguments to complement must be slices or arrays
- can't iterate over a nil value
- symdiff: failed to convert value: %w
- arguments to symdiff must be slices or arrays
- errWrongArgStructure
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/dbec3348886de1bf.json.
Report an issue: GitHub ↗.