{"record":{"id":"5fd1561695aef9d5","repo":"gohugoio/hugo","slug":"expected-a-slice-got-t","errorCode":null,"errorMessage":"expected a slice, got %T","messagePattern":"expected a slice, got %T","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/collections/append.go","lineNumber":44,"sourceCode":"func Append(to any, from ...any) (any, error) {\n\tif len(from) == 0 {\n\t\treturn to, nil\n\t}\n\ttov, toIsNil := hreflect.Indirect(reflect.ValueOf(to))\n\n\ttoIsNil = toIsNil || to == nil\n\tvar tot reflect.Type\n\n\tif !toIsNil {\n\t\tif tov.Kind() == reflect.Slice {\n\t\t\t// Create a copy of tov, so we don't modify the original.\n\t\t\tc := reflect.MakeSlice(tov.Type(), tov.Len(), tov.Len()+len(from))\n\t\t\treflect.Copy(c, tov)\n\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}","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/gohugoio/hugo/blob/52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2/common/collections/append.go#L26-L62","documentation":"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.","triggerScenarios":"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.","commonSituations":"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`.","solutions":["Make sure the first argument is a slice (e.g. []string, []any) or nil/nil slice.","If the value may be a single element, wrap it: `[]any{value}` before appending.","If loading from config/frontmatter, validate the field is a list before calling Append.","For arrays, convert to a slice first via `s[:]`."],"exampleFix":"// before\ncollections.Append(\"hello\", \"world\")\n// after\ncollections.Append([]string{\"hello\"}, \"world\")","handlingStrategy":"type-guard","validationCode":"func isSlice(v any) bool {\n  if v == nil { return true }\n  rv := reflect.ValueOf(v)\n  for rv.Kind() == reflect.Pointer || rv.Kind() == reflect.Interface {\n    if rv.IsNil() { return true }\n    rv = rv.Elem()\n  }\n  return rv.Kind() == reflect.Slice\n}","typeGuard":"func isAppendableSlice(v any) bool { return isSlice(v) }","tryCatchPattern":null,"preventionTips":["Wrap collections.Append calls in a helper that asserts the first arg is a slice.","When loading from config, coerce frontmatter lists with cast before appending.","Never pass structs/maps as the head argument."],"tags":["collections","append","slice","reflection","template"],"backgroundTag":null,"analyzedSha":"52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2","analyzedAt":"2026-08-09T21:49:36.660Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}