bazelbuild/bazel · error · EvalException

unsupported comparison: %s <=> %s

Error message

unsupported comparison: %s <=> %s

What it means

Thrown when a Starlark ordered comparison operator (<, <=, >, >=) is applied to two values whose types cannot be ordered against each other. EvalUtils.compare catches the ClassCastException raised by Starlark.compareUnchecked and rethrows it as an EvalException, so the user sees 'unsupported comparison: T1 <=> T2'. Only same-type ordered values (and int/float mixtures) are comparable in Starlark.

Source

Thrown at src/main/java/net/starlark/java/eval/EvalUtils.java:408

      }
    }
    if (y instanceof HasBinary) {
      Object z = ((HasBinary) y).binaryOp(op, x, false);
      if (z != null) {
        return z;
      }
    }

    throw Starlark.errorf(
        "unsupported binary operation: %s %s %s", Starlark.type(x), op, Starlark.type(y));
  }

  // Defines the behavior of the language's ordered comparison operators (< <= => >).
  private static int compare(Object x, Object y) throws EvalException {
    try {
      return Starlark.compareUnchecked(x, y);
    } catch (ClassCastException ex) {
      throw new EvalException(ex.getMessage());
    }
  }

  private static String repeatString(String s, StarlarkInt in) throws EvalException {
    int n = in.toInt("repeat");
    if (n <= 0) {
      return "";
    } else if ((long) s.length() * (long) n > Integer.MAX_VALUE) {
      // Would exceed max length of a java String.
      throw Starlark.errorf("excessive repeat (%d * %d characters)", s.length(), n);
    } else {
      return s.repeat(n);
    }
  }

  /** Evaluates a unary operation. */
  static Object unaryOp(TokenKind op, Object x) throws EvalException {
    switch (op) {

View on GitHub (pinned to e6e199d060)

Solutions

  1. Ensure both operands have the same type before comparing; convert explicitly (str(x) < str(y), int(x) < int(y)).
  2. If comparing containers, compare element-wise or via a key function that yields a uniform type (e.g. sorted(items, key=str)).
  3. For 'unset' checks, test identity/emptiness (x == None, if not x) instead of ordering against None.
  4. Catch EvalException at the Java call site and surface a domain-specific message.

Example fix

# before
if version < 2:  # version is "1.2.3"
    ...

# after
if int(version.split(".")[0]) < 2:
    ...
Defensive patterns

Strategy: validation

Validate before calling

# before comparing, ensure both sides are the same type
def safe_lt(x, y):
    if type(x) != type(y) and not ((type(x) == "int" and type(y) == "float") or (type(x) == "float" and type(y) == "int")):
        fail("cannot compare %s with %s" % (type(x), type(y)))
    return x < y

Type guard

def is_comparable_pair(x, y):
    t = (type(x), type(y))
    return t[0] == t[1] or set(t) == {"int", "float"}

Prevention

When it happens

Trigger: Evaluating 1 < "a", [1,2] < (1,2), None < 1, or any < between values of different Starlark types where neither side is an int/float pair. Also fires when a user-defined Comparable Starlark value is compared against an unrelated type.

Common situations: Sorting or comparing heterogeneous lists (e.g. sorted(["b", 1])), validating input where a string arrives where an int was expected, comparing depsets/tuples/lists across types, or comparing None to a number as a 'is it unset' idiom borrowed from other languages.

Related errors


AI-assisted analysis of bazelbuild/bazel@e6e199d060 (2026-08-14). Data as JSON: /api/errors/4b92fcdbc9861e7a. Report an issue: GitHub.