gohugoio/hugo · error

incompatible types for comparison: %v and %v

Error message

incompatible types for comparison: %v and %v

What it means

Raised by the `eq` template function when both operands are valid (non-nil) but their basic kinds differ and are not the int/uint cross-sign special case (funcs.go:478-481). `eq` only compares like kinds (after the integer cross-sign allowance); mismatches like string-vs-int or bool-vs-string are rejected. Format: `type1 type2`.

Source

Thrown at tpl/internal/go_templates/texttemplate/funcs.go:480

	arg1 = indirectInterface(arg1)
	if len(arg2) == 0 {
		return false, errNoComparison
	}
	k1, _ := basicKind(arg1)
	for _, arg := range arg2 {
		arg = indirectInterface(arg)
		k2, _ := basicKind(arg)
		truth := false
		if k1 != k2 {
			// Special case: Can compare integer values regardless of type's sign.
			switch {
			case k1 == intKind && k2 == uintKind:
				truth = arg1.Int() >= 0 && uint64(arg1.Int()) == arg.Uint()
			case k1 == uintKind && k2 == intKind:
				truth = arg.Int() >= 0 && arg1.Uint() == uint64(arg.Int())
			default:
				if arg1.IsValid() && arg.IsValid() {
					return false, fmt.Errorf("incompatible types for comparison: %v and %v", arg1.Type(), arg.Type())
				}
			}
		} else {
			switch k1 {
			case boolKind:
				truth = arg1.Bool() == arg.Bool()
			case complexKind:
				truth = arg1.Complex() == arg.Complex()
			case floatKind:
				truth = arg1.Float() == arg.Float()
			case intKind:
				truth = arg1.Int() == arg.Int()
			case stringKind:
				truth = arg1.String() == arg.String()
			case uintKind:
				truth = arg1.Uint() == arg.Uint()
			default:
				if !canCompare(arg1, arg) {

View on GitHub (pinned to 52c9bd7908)

Solutions

  1. Coerce both sides to the same type before comparing: `{{eq (int .Count) 5}}` or `{{eq (printf "%v" .Count) "5"}}`.
  2. Make the data layer produce consistently typed values.
  3. Use the appropriate builtin (`float`, `int`, `string`) to normalize.

Example fix

// before
{{eq .Qty "5"}}   // .Qty is int

// after
{{eq (printf "%d" .Qty) "5"}}
Defensive patterns

Strategy: type-guard

Validate before calling

// Normalize both operands to the same kind before eq:
//   {{eq (int .Qty) 5}}
//   {{eq (printf "%d" .Qty) "5"}}

Type guard

func sameBasicKind(a, b interface{}) bool {
    ka, _ := basicKindOf(a)
    kb, _ := basicKindOf(b)
    return ka == kb || (ka == "int" && kb == "uint") || (ka == "uint" && kb == "int")
}
// where basicKindOf maps via reflect as in funcs.go:418

Prevention

When it happens

Trigger: `{{eq .Count "5"}}` (int vs string); `{{eq .Flag 1}}` (bool vs int); `{{eq .Name 3}}` (string vs int); comparing a number from JSON to a string literal in template.

Common situations: Front matter values loaded as strings (YAML/csv) compared to numeric literals; JSON numbers vs template string literals; loose typing crossing module boundaries.

Related errors


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