{"id":"dbec3348886de1bf","repo":"gohugoio/hugo","slug":"cannot-append-slice-of-s-to-slice-of-s","errorCode":null,"errorMessage":"cannot append slice of %s to slice of %s","messagePattern":"cannot append slice of (.+?) to slice of (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/collections/append.go","lineNumber":58,"sourceCode":"\t\t\ttov = c\n\t\t}\n\n\t\tif tov.Kind() != reflect.Slice {\n\t\t\treturn nil, fmt.Errorf(\"expected a slice, got %T\", to)\n\t\t}\n\n\t\ttot = tov.Type().Elem()\n\t\tif tot.Kind() == reflect.Slice {\n\t\t\ttotvt := tot.Elem()\n\t\t\tfromvs := make([]reflect.Value, len(from))\n\t\t\tfor i, f := range from {\n\t\t\t\tfromv := reflect.ValueOf(f)\n\t\t\t\tfromt := fromv.Type()\n\t\t\t\tif fromt.Kind() == reflect.Slice {\n\t\t\t\t\tfromt = fromt.Elem()\n\t\t\t\t}\n\t\t\t\tif totvt != fromt {\n\t\t\t\t\treturn nil, fmt.Errorf(\"cannot append slice of %s to slice of %s\", fromt, totvt)\n\t\t\t\t} else {\n\t\t\t\t\tfromvs[i] = fromv\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn reflect.Append(tov, fromvs...).Interface(), nil\n\n\t\t}\n\n\t\ttoIsNil = tov.Len() == 0\n\n\t\tif len(from) == 1 {\n\t\t\tfromv := reflect.ValueOf(from[0])\n\t\t\tif !fromv.IsValid() {\n\t\t\t\t// from[0] is nil\n\t\t\t\treturn appendToInterfaceSliceFromValues(tov, fromv)\n\t\t\t}\n\t\t\tfromt := fromv.Type()\n\t\t\tif fromt.Kind() == reflect.Slice {","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/gohugoio/hugo/blob/8a468df065a75c1c7cf9f6850f32148746590ea5/common/collections/append.go#L40-L76","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"{{/* before */}}\n{{ $rows := slice (slice \"a\" \"b\") }}\n{{ $rows = $rows | append (slice 1 2) }}  {{/* error */}}\n\n{{/* after */}}\n{{ $rows := slice (slice \"a\" \"b\") }}\n{{ $rows = $rows | append (slice \"1\" \"2\") }}","handlingStrategy":"type-guard","validationCode":"// Go caller: ensure element types match before append\nif reflect.TypeOf(elem) != reflect.TypeOf(slice).Elem() { /* wrap in []interface{} or convert first */ }","typeGuard":"func canAppend(slice, elem any) bool {\n\tsv := reflect.ValueOf(slice)\n\tif sv.Kind() != reflect.Slice { return false }\n\tet := sv.Type().Elem()\n\treturn et.Kind() == reflect.Interface || reflect.TypeOf(elem).AssignableTo(et)\n}","tryCatchPattern":"res, err := collections.Append(to, from...)\nif err != nil {\n\t// mixed element types: rebuild target as []interface{} and retry once\n}","preventionTips":["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`."],"tags":["hugo","templates","collections","reflection","type-mismatch"],"analyzedSha":"8a468df065a75c1c7cf9f6850f32148746590ea5","analyzedAt":"2026-07-31T21:23:07.045Z","schemaVersion":2}