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
- Coerce both sides to the same type before comparing: `{{eq (int .Count) 5}}` or `{{eq (printf "%v" .Count) "5"}}`.
- Make the data layer produce consistently typed values.
- 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
- Normalize front-matter values to typed fields at the data boundary.
- Use int/float/string builtins to coerce before comparing.
- Document expected operand types in template comments.
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
- non-comparable types %s: %v, %s: %v
- non-comparable type %s: %v
- arg %d: %w
- argument must be a slice
- union does not support slices or arrays of uncomparable type
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/653da946e128b4dc.
Report an issue: GitHub.