{"record":{"id":"c8aff4eb1418b4f0","repo":"gohugoio/hugo","slug":"union-does-not-support-slices-or-arrays-of-uncompa","errorCode":null,"errorMessage":"union does not support slices or arrays of uncomparable types","messagePattern":"union does not support slices or arrays of uncomparable types","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tpl/collections/collections.go","lineNumber":672,"sourceCode":"\t\tcase reflect.Array, reflect.Slice:\n\t\t\tins = &intersector{r: reflect.MakeSlice(l1v.Type(), 0, 0), seen: make(map[any]bool)}\n\n\t\t\tif l1v.Type() != l2v.Type() &&\n\t\t\t\tl1v.Type().Elem().Kind() != reflect.Interface &&\n\t\t\t\tl2v.Type().Elem().Kind() != reflect.Interface {\n\t\t\t\treturn ins.r.Interface(), nil\n\t\t\t}\n\n\t\t\tvar (\n\t\t\t\tl1vv  reflect.Value\n\t\t\t\tisNil bool\n\t\t\t)\n\n\t\t\tfor i := range l1v.Len() {\n\t\t\t\tl1vv, isNil = hreflect.Indirect(l1v.Index(i))\n\n\t\t\t\tif !l1vv.Type().Comparable() {\n\t\t\t\t\treturn []any{}, errors.New(\"union does not support slices or arrays of uncomparable types\")\n\t\t\t\t}\n\n\t\t\t\tif !isNil {\n\t\t\t\t\tins.appendIfNotSeen(l1vv)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif !l1vv.IsValid() {\n\t\t\t\t// The first slice may be empty. Pick the first value of the second\n\t\t\t\t// to use as a prototype.\n\t\t\t\tif l2v.Len() > 0 {\n\t\t\t\t\tl1vv = l2v.Index(0)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor j := range l2v.Len() {\n\t\t\t\tl2vv := l2v.Index(j)\n\t\t\t\ttyp := l1vv.Type()","sourceCodeStart":654,"sourceCodeEnd":690,"githubUrl":"https://github.com/gohugoio/hugo/blob/52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2/tpl/collections/collections.go#L654-L690","documentation":"Thrown by Union when an element type of the input slices is not Comparable (line 671). Union deduplicates using a map keyed by element values, which requires element types to be comparable (no slices, maps, or funcs as elements). The check runs per element of l1.","triggerScenarios":"Calling {{ union $a $b }} where $a/$b are [][]int, []map[string]string, or contain func/interface-of-uncomparable values; piping slices-of-slices into union.","commonSituations":"Trying to union nested data structures; refactoring a flat string slice into a slice of structs with unexported fields (structs with unexported fields are not comparable via ==, though they are via reflect Comparable in some cases); data model changes introducing slices-of-maps.","solutions":["Flatten or project to a slice of comparable scalars (strings/ints) before union, e.g. extract a key field per element into []string.","Use a custom dedup via scratch/range if you need to union complex elements.","Confirm element types are comparable (strings, numbers, pointers, comparable structs) before calling union."],"exampleFix":"// before\n{{ union $sliceOfMapsA $sliceOfMapsB }}\n// after\n{{ $keys := slice }}{{ range $sliceOfMapsA }}{{ $keys = $keys | append .id }}{{ end }}\n{{ range $sliceOfMapsB }}{{ $keys = $keys | append .id }}{{ end }}\n{{ uniq $keys }}","handlingStrategy":"type-guard","validationCode":"{{/* project to comparable scalars before union */}}\n{{ $keys := slice }}{{ range $a }}{{ $keys = $keys | append .id }}{{ end }}\n{{ range $b }}{{ $keys = $keys | append .id }}{{ end }}\n{{ uniq $keys }}","typeGuard":"// In Go:\n// func elemComparable(v any) bool {\n//   if v == nil { return false }\n//   rv := reflect.ValueOf(v)\n//   if rv.Kind() != reflect.Slice && rv.Kind() != reflect.Array { return false }\n//   return rv.Type().Elem().Comparable()\n// }","tryCatchPattern":null,"preventionTips":["Union only supports slices/arrays of comparable element types (no slices/maps/funcs as elements).","Project complex elements down to a comparable key field before union.","Avoid slices-of-maps/slices-of-slices with union."],"tags":["hugo","template","collections","union","type-mismatch","comparable"],"backgroundTag":null,"analyzedSha":"52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2","analyzedAt":"2026-08-09T21:49:36.660Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}