{"id":"126e4be5d53d0913","repo":"gohugoio/hugo","slug":"complement-needs-at-least-two-arguments","errorCode":null,"errorMessage":"complement needs at least two arguments","messagePattern":"complement needs at least two arguments","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tpl/collections/complement.go","lineNumber":34,"sourceCode":"import (\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\n\t\"github.com/gohugoio/hugo/common/hreflect\"\n)\n\n// Complement gives the elements in the last element of ls that are not in\n// any of the others.\n//\n// All elements of ls must be slices or arrays of comparable types.\n//\n// The reasoning behind this rather clumsy API is so we can do this in the templates:\n//\n//\t{{ $c := .Pages | complement $last4 }}\nfunc (ns *Namespace) Complement(ls ...any) (any, error) {\n\tif len(ls) < 2 {\n\t\treturn nil, errors.New(\"complement needs at least two arguments\")\n\t}\n\n\tuniverse := ls[len(ls)-1]\n\tas := ls[:len(ls)-1]\n\n\taset, err := collectIdentities(as...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tv := reflect.ValueOf(universe)\n\tswitch v.Kind() {\n\tcase reflect.Array, reflect.Slice:\n\t\tsl := reflect.MakeSlice(v.Type(), 0, 0)\n\t\tfor i := range v.Len() {\n\t\t\tev, _ := hreflect.Indirect(v.Index(i))\n\t\t\tif _, found := aset[normalize(ev)]; !found {\n\t\t\t\tsl = reflect.Append(sl, ev)","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/gohugoio/hugo/blob/8a468df065a75c1c7cf9f6850f32148746590ea5/tpl/collections/complement.go#L16-L52","documentation":"`collections.Complement` returns the elements of its last argument that appear in none of the earlier ones (tpl/collections/complement.go:32-35). With fewer than two arguments there is no universe/exclusion pair to compute — one collection alone has no complement — so Hugo rejects the call up front. The last-argument-is-the-universe design exists so it pipelines naturally: `{{ .Pages | complement $last4 }}`.","triggerScenarios":"Calling `{{ complement .Pages }}` with a single collection, or `{{ complement }}` with none — typically by not realizing the piped value counts as the final argument and at least one exclusion set must be passed before it.","commonSituations":"Misreading the docs and expecting complement of one set against the site automatically; a template refactor that drops the exclusion variable; passing the exclusion set conditionally so it's absent on some pages.","solutions":["Provide at least one exclusion collection plus the universe: `{{ $rest := .Pages | complement $featured }}`.","If the exclusion set can be empty, still pass it as an empty slice (`default (slice)`) rather than omitting the argument.","If you didn't intend a set operation, use `where` or `first` instead."],"exampleFix":"<!-- before -->\n{{ $rest := complement .Pages }}\n<!-- after -->\n{{ $featured := first 4 .Pages }}\n{{ $rest := .Pages | complement $featured }}","handlingStrategy":"validation","validationCode":"{{ if lt (len $sets) 2 }}\n  {{ errorf \"complement needs at least two collections, got %d\" (len $sets) }}\n{{ end }}\n{{ $result := collections.Complement $exclude $universe }}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass at least two collections: the last argument is the source, earlier ones are excluded","When building the argument list dynamically, check its length ≥ 2 first","Remember argument order — `complement $toRemove $all`, not the reverse"],"tags":["hugo","templates","collections","complement"],"analyzedSha":"8a468df065a75c1c7cf9f6850f32148746590ea5","analyzedAt":"2026-07-31T21:23:07.045Z","schemaVersion":2}