gohugoio/hugo · error
uneven number of replacement pairs
Error message
uneven number of replacement pairs
What it means
`strings.ReplacePairs` builds a `strings.Replacer` from the collected pair list, which requires old/new values in matched pairs — an odd count means some old value has no replacement. Hugo validates `len(p)%2 != 0` before constructing the Replacer, since `strings.NewReplacer` would otherwise panic.
Source
Thrown at tpl/strings/strings.go:301
}
if p == nil {
// inline form: ReplacePairs "a" "b" "s"
p = make([]string, len(args)-1)
for i, v := range args[:len(args)-1] {
s, err := cast.ToStringE(v)
if err != nil {
return "", err
}
p[i] = s
}
}
if len(p) == 0 || ss == "" {
return ss, nil
}
if len(p)%2 != 0 {
return "", fmt.Errorf("uneven number of replacement pairs")
}
key := strings.Join(p, "\x00")
replacer, err := ns.replacerCache.GetOrCreate(key, func() (*strings.Replacer, error) {
return strings.NewReplacer(p...), nil
})
if err != nil {
return "", err
}
return replacer.Replace(ss), nil
}
// SliceString slices a string by specifying a half-open range with
// two indices, start and end. 1 and 4 creates a slice including elements 1 through 3.
// The end index can be omitted, it defaults to the string's length.
func (ns *Namespace) SliceString(a any, startEnd ...any) (string, error) {
aStr, err := cast.ToStringE(a)View on GitHub ↗ (pinned to 8a468df065)
Solutions
- Count the pair elements — every old string needs a new string; add the missing element.
- Check you didn't omit the trailing source argument: the last argument is always the source, not part of a pair.
- If pairs come from a data file, validate the list length is even at the source.
Example fix
<!-- before -->
{{ strings.ReplacePairs (slice "a" "A" "b") $src }}
<!-- after -->
{{ strings.ReplacePairs (slice "a" "A" "b" "B") $src }} Defensive patterns
Strategy: validation
Validate before calling
{{ if eq (mod (len $pairs) 2) 0 }}{{ strings.Replacer $s $pairs }}{{ else }}{{ errorf "replacement pairs uneven: %v" $pairs }}{{ end }} Type guard
func evenPairs(pairs []string) bool { return len(pairs)%2 == 0 } Try / catch
out, err := ns.Replacer(args...)
if err != nil {
// odd pair count — one old is missing its new; fix config, don't drop the last element
} Prevention
- Check `len % 2 == 0` on replacement lists sourced from config/data
- Define pairs as a list of two-element entries in config and flatten at use, making unevenness impossible
- Fail the build with `errorf` on uneven pairs instead of silently truncating
When it happens
Trigger: An odd number of pair elements: inline form `{{ strings.ReplacePairs "a" "b" "c" $src }}` (three pair args), or slice form `{{ strings.ReplacePairs (slice "a" "b" "c") $src }}`. Empty pairs skip this check (the source is returned unchanged), so only genuinely odd counts trigger it.
Common situations: Forgetting the source string goes last, so the last replacement value gets consumed as the source and the remaining pairs become odd; dropping one element when editing a YAML list of pairs in front matter; programmatically appending to the pairs slice and missing a value.
Related errors
- requires at least 2 arguments
- with 2 arguments, the first must be a slice of replacement p
- errMustTwoNumbersError
- failed to convert content to string: %w
- failed to match regex pattern against string: %w
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/116be4c7f928532d.json.
Report an issue: GitHub ↗.