gohugoio/hugo · error
arguments to complement must be slices or arrays
Error message
arguments to complement must be slices or arrays
What it means
Hugo's `complement` template function returns the elements of the last argument (the "universe") that are not present in the earlier arguments. After collecting identities from the earlier arguments, it reflects on the final argument and requires reflect.Kind Array or Slice; any other kind (map, string, Page, int, nil) hits the default case at tpl/collections/complement.go:57 and returns this error.
Source
Thrown at tpl/collections/complement.go:57
aset, err := collectIdentities(as...)
if err != nil {
return nil, err
}
v := reflect.ValueOf(universe)
switch v.Kind() {
case reflect.Array, reflect.Slice:
sl := reflect.MakeSlice(v.Type(), 0, 0)
for i := range v.Len() {
ev, _ := hreflect.Indirect(v.Index(i))
if _, found := aset[normalize(ev)]; !found {
sl = reflect.Append(sl, ev)
}
}
return sl.Interface(), nil
default:
return nil, fmt.Errorf("arguments to complement must be slices or arrays")
}
}
View on GitHub ↗ (pinned to 8a468df065)
Solutions
- Ensure the last argument (the piped value when using `|`) is a slice or array, e.g. `.Pages`, `slice ...`, or `.Site.RegularPages`.
- Check argument order: `{{ $universe | complement $exclusions }}` — the universe must be the final/piped argument.
- Guard against nil/missing collections with `with` or a default: `{{ $c := .Pages | complement (default (slice) $last4) }}`.
Example fix
<!-- before: universe is a single page -->
{{ $c := .Page | complement $featured }}
<!-- after: universe is a page collection -->
{{ $c := .Site.RegularPages | complement $featured }} Defensive patterns
Strategy: type-guard
Validate before calling
{{/* ensure every argument is a slice/array before complement */}}
{{ if and (reflect.IsSlice .setA) (reflect.IsSlice .setB) }}
{{ $rest := complement .setA .setB }}
{{ end }} Type guard
{{ define "isCollection" }}{{ or (reflect.IsSlice .) (reflect.IsMap .) }}{{ end }}
{{/* complement requires slices/arrays specifically: */}}
{{ $ok := reflect.IsSlice $candidate }} Prevention
- Check reflect.IsSlice on each argument before passing it to complement
- Watch for single values sneaking in — wrap scalars with (slice $v) if needed
- Remember .Params entries may be scalars, not slices, depending on front matter shape
- Use printf "%T" $v with hdebug/warnf while developing to confirm argument types
When it happens
Trigger: Calling `{{ complement $a $b }}` or `{{ $x | complement $y }}` in a Hugo template where the last argument (the piped value in the pipe form) is not a slice or array — e.g. a single Page, a map from `dict`, a string, or nil from a failed lookup.
Common situations: Piping a single page instead of a page collection (`{{ .Page | complement $x }}`), passing `.Params.something` that is a map or missing (nil), or reversing argument order and putting the exclusion set last while the universe is a scalar. Also happens when a `where` result is nil because the query matched nothing of the expected shape.
Related errors
- cannot append slice of %s to slice of %s
- symdiff: failed to convert value: %w
- arguments to symdiff must be slices or arrays
- errWrongArgStructure
- errKeyIsEmptyString
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/7ec1820653cae584.json.
Report an issue: GitHub ↗.