gohugoio/hugo · error

invalid type for comparison

Error message

invalid type for comparison

What it means

Defined as errBadComparisonType in tpl/internal/go_templates/texttemplate/funcs.go:402 and returned by the template comparison operators (eq, ne, lt, le, gt, ge). It fires when basicKind() cannot classify an operand (e.g. a struct, map, slice, or func) or when lt/le/gt/ge is applied to bool or complex values, which are not orderable. This is the underlying text/template engine reused by Hugo.

Source

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

}

// or computes the Boolean OR of its arguments, returning
// the first true argument it encounters, or the last argument.
func or(arg0 reflect.Value, args ...reflect.Value) reflect.Value {
	panic("unreachable") // implemented as a special case in evalCall
}

// not returns the Boolean negation of its argument.
func not(arg reflect.Value) bool {
	return !truth(arg)
}

// Comparison.

// TODO: Perhaps allow comparison between signed and unsigned integers.

var (
	errBadComparisonType = errors.New("invalid type for comparison")
	errNoComparison      = errors.New("missing argument for comparison")
)

type kind int

const (
	invalidKind kind = iota
	boolKind
	complexKind
	intKind
	floatKind
	stringKind
	uintKind
)

func basicKind(v reflect.Value) (kind, error) {
	switch v.Kind() {
	case reflect.Bool:

View on GitHub (pinned to 52c9bd7908)

Solutions

  1. Compare a comparable field: use .Date, .Weight, .RelPermalink, .Title, etc. instead of the whole object.
  2. For ordering, ensure both operands are bool/int/uint/float/string and the same kind (int vs uint has a special case, others must match).
  3. For equality on structs/maps, compare a unique key field or use a custom approach (e.g. .File.Path).
  4. Do not use lt/le/gt/ge on bool or complex values; convert booleans to ints if you need ordering.

Example fix

// before
{{ if lt .Page .OtherPage }}

// after
{{ if lt .Date .OtherPage.Date }}
Defensive patterns

Strategy: validation

Validate before calling

// Compare a comparable field rather than whole objects:
{{ if lt .Date .OtherPage.Date }}...{{ end }}
// Avoid lt/le/gt/ge on bool or complex; for equality on primitives only.

Type guard

// Go caller: only bool/int/uint/float/string/complex are comparable via basicKind.
func orderable(k reflect.Kind) bool {
    switch k {
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
        reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
        reflect.Float32, reflect.Float64, reflect.String:
        return true
    }
    return false
}

Prevention

When it happens

Trigger: Writing {{ if lt .Page .OtherPage }} (comparing structs), {{ if lt $map1 $map2 }} (comparing maps), {{ if eq $slice1 $slice2 }} with non-comparable slices, or {{ if lt .Flag true }} (ordering booleans). Also comparing a struct field that is itself a non-basic type.

Common situations: Comparing page objects instead of a comparable field like .RelPermalink or .Date; assuming eq works on arbitrary structs; ordering booleans; comparing complex128 values.

Related errors


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