bazelbuild/bazel · error · EvalException
unsupported comparison: %s <=> %s
Error message
unsupported comparison: %s <=> %s
What it means
Thrown by the built-in min()/max() when the elements being ranked are mutually incomparable. The ordering used by maxOrdering.max(items) (or by the comparison-key comparator when key= is given) throws ClassCastException, which MethodLibrary catches and converts to EvalException with the message 'unsupported comparison: T1 <=> T2'.
Source
Thrown at src/main/java/net/starlark/java/eval/MethodLibrary.java:139
try {
EvalUtils.addIterator(items); // to prevent keyFn from mutating items
if (keyFn.isPresent()) {
try {
return stream(items)
.map(value -> ValueWithComparisonKey.make(value, keyFn.get(), thread))
.max(comparing(ValueWithComparisonKey::getComparisonKey, maxOrdering))
.get()
.getValue();
} catch (ValueWithComparisonKey.KeyCallException ex) {
Throwables.throwIfInstanceOf(ex.getCause(), EvalException.class);
Throwables.throwIfInstanceOf(ex.getCause(), InterruptedException.class);
throw new AssertionError("Got invalid ValueWithComparisonKey.KeyCallException", ex);
}
} else {
return maxOrdering.max(items);
}
} catch (ClassCastException ex) {
throw new EvalException(ex.getMessage()); // e.g. unsupported comparison: int <=> string
} catch (NoSuchElementException ex) {
throw new EvalException("expected at least one item", ex);
} finally {
EvalUtils.removeIterator(items);
}
}
/**
* Original value decorated with its comparison key; storing the comparison key alongside the
* value ensures that we call the comparison key computation function only once per original value
* (which is important in case the function has side effects).
*/
private static final class ValueWithComparisonKey {
private final Object value;
private final Object comparisonKey;
private ValueWithComparisonKey(Object value, Object comparisonKey) {
this.value = value;View on GitHub (pinned to e6e199d060)
Solutions
- Normalize elements (or key results) to one type before calling min/max: max(items, key=lambda x: int(x)).
- Make the key= function total: map None/missing sentinels to a sortable fallback such as float("-inf") or "".
- Pre-filter or pre-validate the list so all elements share a type.
- Catch EvalException around the min()/max() call and degrade gracefully.
Example fix
# before
m = max(vals, key=lambda v: v.score) # score is None for some items
# after
m = max(vals, key=lambda v: v.score if v.score != None else float("-inf")) Defensive patterns
Strategy: validation
Validate before calling
# ensure key function yields one uniform type before min/max
keys = [k(v) for v in items]
if len({type(k) for k in keys}) > 1:
fail("min/max over mixed key types")
m = max(items, key=k) Type guard
def uniform_types(items):
t = type(items[0]) if items else None
return t != None and all(type(i) == t for i in items) Prevention
- Make key= functions total: never return None or mixed types.
- Validate list homogeneity once at the boundary where the list is built.
- Unit-test min/max on empty and heterogeneous fixtures.
When it happens
Trigger: max([1, "a", 2]), min((None, 1)), or max(items, key=lambda x: x.attr) where the key function returns mixed types for different elements (e.g. int for one element, string for another).
Common situations: Computing a max over user-supplied or file-derived data whose types are not guaranteed uniform; a key= function that returns None or an empty string as a sentinel for some elements; aggregating over a list built by concatenating heterogeneous sources.
Related errors
- unsupported comparison: %s <=> %s
- expected at least one item
- got %s for '%%%c' format, want int or float
- cannot compare self-referential or overly nested data struct
- unsupported comparison: %s <=> %s
AI-assisted analysis of bazelbuild/bazel@e6e199d060 (2026-08-14).
Data as JSON: /api/errors/7cfdf7307066868b.
Report an issue: GitHub.