gohugoio/hugo · error

uneven number of replacement pairs

Error message

uneven number of replacement pairs

What it means

Thrown by strings.ReplacePairs after collecting all pair elements when len(p) is odd. strings.NewReplacer requires old/new pairs, so an odd count means one element has no partner.

Solutions

  1. Ensure replacement pairs are balanced: every old value has a following new value.
  2. In slice form, check len(slice) is even before passing.
  3. Add the missing partner value for the trailing entry.

Example fix

// before (odd number of pairs):
{{ strings.ReplacePairs "a" "b" "c" $s }}
// after (balanced pairs):
{{ strings.ReplacePairs "a" "b" "c" "d" $s }}
Defensive patterns

Strategy: validation

Validate before calling

{{ if eq (mod (len $pairs) 2) 0 }}{{ strings.ReplacePairs $pairs $s }}{{ else }}{{ warnf "odd pairs" }}{{ end }}

Prevention

When it happens

Trigger: Inline form with an odd number of pair args (e.g. strings.ReplacePairs "a" "b" "c" $s) or a slice form whose length is odd (e.g. (slice "a" "b" "c")). The check runs after argument normalization, before constructing the Replacer.

Common situations: Forgetting the 'new' value for the last replacement, or a slice built programmatically with an odd number of entries.

Related errors


AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09). Data as JSON: /api/errors/116be4c7f928532d. Report an issue: GitHub.

Appendix: 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 52c9bd7908)