{"id":"96926240574b4ed8","repo":"gohugoio/hugo","slug":"elements-must-be-comparable","errorCode":null,"errorMessage":"elements must be comparable","messagePattern":"elements must be comparable","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tpl/collections/reflect_helpers.go","lineNumber":67,"sourceCode":"\t\treturn ip.TransientKey()\n\t}\n\n\treturn vv\n}\n\n// collects identities from the slices in seqs into a set. Numeric values are normalized,\n// pointers unwrapped.\nfunc collectIdentities(seqs ...any) (map[any]bool, error) {\n\tseen := make(map[any]bool)\n\tfor _, seq := range seqs {\n\t\tv := reflect.ValueOf(seq)\n\t\tswitch v.Kind() {\n\t\tcase reflect.Array, reflect.Slice:\n\t\t\tfor i := range v.Len() {\n\t\t\t\tev, _ := hreflect.Indirect(v.Index(i))\n\n\t\t\t\tif !ev.Type().Comparable() {\n\t\t\t\t\treturn nil, errors.New(\"elements must be comparable\")\n\t\t\t\t}\n\n\t\t\t\tseen[normalize(ev)] = true\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"arguments must be slices or arrays\")\n\t\t}\n\t}\n\n\treturn seen, nil\n}\n\n// We have some different numeric and string types that we try to behave like\n// they were the same.\nfunc convertValue(v reflect.Value, to reflect.Type) (reflect.Value, error) {\n\tif v.Type().AssignableTo(to) {\n\t\treturn v, nil\n\t}","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/gohugoio/hugo/blob/8a468df065a75c1c7cf9f6850f32148746590ea5/tpl/collections/reflect_helpers.go#L49-L85","documentation":"Set-like collection functions in Hugo (union, intersect, symdiff, uniq — anything that builds an identity set via `collectIdentities`) must place each element in a Go map key. That requires the element's type to be comparable per Go's rules; slices, maps, and funcs (or structs containing them) are not, so Hugo rejects the operation.","triggerScenarios":"Calling `union`, `intersect`, `symdiff`, or `uniq` on collections whose elements are themselves slices or maps — e.g. `{{ symdiff (slice (slice 1 2)) (slice (slice 3)) }}` or deduplicating a slice of dicts/param maps.","commonSituations":"Trying to `uniq`/`intersect` slices of `dict` values or front-matter param maps; nested data from data files (arrays of arrays from JSON/YAML); comparing lists of taxonomy term maps instead of the term strings.","solutions":["Operate on a comparable key extracted from each element instead of the whole element — e.g. use `where`, or build slices of strings/ids first (`{{ $ids := slice }}{{ range $items }}{{ $ids = $ids | append .id }}{{ end }}`), then apply uniq/intersect to those.","For Pages collections, use the page-aware set functions on the Pages themselves (Pages are comparable via identity), not on derived maps.","Restructure data files so the compared values are scalars (strings/numbers) rather than nested arrays/maps."],"exampleFix":"<!-- before -->\n{{ $u := uniq (slice (dict \"id\" 1) (dict \"id\" 1)) }}\n<!-- after: dedupe by scalar key -->\n{{ $ids := slice }}{{ range $items }}{{ $ids = $ids | append .id }}{{ end }}\n{{ $u := uniq $ids }}","handlingStrategy":"type-guard","validationCode":"{{ $s := slice 1 2 3 }}\n{{ $comparable := true }}\n{{ range $s }}{{ if or (reflect.IsMap .) (reflect.IsSlice .) }}{{ $comparable = false }}{{ end }}{{ end }}\n{{ if $comparable }}{{ uniq $s }}{{ end }}","typeGuard":"{{ $isComparable := not (or (reflect.IsMap $el) (reflect.IsSlice $el)) }}","tryCatchPattern":"{{ with try (uniq $s) }}{{ with .Err }}{{ warnf \"uniq needs comparable elements: %s\" . }}{{ else }}{{ .Value }}{{ end }}{{ end }}","preventionTips":["Functions like uniq, intersect, union, in require scalar (comparable) elements — not maps or slices","When working with page or dict collections, extract a comparable key first (e.g. map to .Permalink or an id) before set operations","Use where/complement for filtering collections of maps instead of set operations on the maps themselves"],"tags":["hugo","collections","templates","go-reflection"],"analyzedSha":"8a468df065a75c1c7cf9f6850f32148746590ea5","analyzedAt":"2026-07-31T21:23:07.045Z","schemaVersion":2}