bazelbuild/bazel · error · ClassCastException

cannot compare self-referential or overly nested data struct

Error message

cannot compare self-referential or overly nested data structures %s and %s

What it means

Starlark.compareUnchecked compares same-type Comparable values by calling compareTo; a self-referential structure (a list containing itself) or one nested deeply enough makes the recursive comparison overflow the Java stack. The StackOverflowError is caught and converted to a ClassCastException with this message (a documented wart: ClassCastException is the channel for all cannot-compare errors), which callers like EvalUtils.compare then turn into an EvalException.

Source

Thrown at src/main/java/net/starlark/java/eval/Starlark.java:595

  /**
   * Defines the strict weak ordering of Starlark values used for sorting and the comparison
   * operators.
   *
   * @throws ClassCastException on failure.
   */
  static int compareUnchecked(Object x, Object y) {
    if (sameType(x, y)) {
      // Ordered? e.g. string, int, bool, float.
      if (x instanceof Comparable) {
        @SuppressWarnings("unchecked")
        Comparable<Object> xcomp = (Comparable<Object>) x;
        try {
          return xcomp.compareTo(y);
        } catch (StackOverflowError unused) {
          // Wart: this particular error has nothing to do with class mismatch - but alas,
          // Comparable interface uses ClassCastException for reporting all cannot-compare errors.
          throw new ClassCastException(
              String.format(
                  "cannot compare self-referential or overly nested data structures %s and %s",
                  Starlark.reprForErrors(x), Starlark.reprForErrors(y)));
        }
      }

    } else {
      // different types

      if (x instanceof StarlarkFloat && y instanceof StarlarkInt) {
        // float < int
        double xf = ((StarlarkFloat) x).toDouble();
        return Double.isNaN(xf) ? +1 : -StarlarkInt.compareIntAndDouble((StarlarkInt) y, xf);
      } else if (x instanceof StarlarkInt && y instanceof StarlarkFloat) {
        // int < float
        double yf = ((StarlarkFloat) y).toDouble();
        return Double.isNaN(yf) ? -1 : StarlarkInt.compareIntAndDouble((StarlarkInt) x, yf);
      }

View on GitHub (pinned to e6e199d060)

Solutions

  1. Compare by a derived scalar key instead of the whole structure: sorted(nodes, key=lambda n: n.id).
  2. Break cycles: never insert a container into itself; store indices/labels instead of direct references.
  3. If cycles are required, compare along an explicit field path rather than recursively.

Example fix

# before
x = []
x.append(x)
sorted([x, []])  # recursive compare -> stack overflow

# after
x = []
x.append("self")
sorted([x, []], key=len)
Defensive patterns

Strategy: validation

Validate before calling

# never compare containers that may be cyclic; compare a derived key
key = lambda n: n.id  # scalar key, safe under recursion
m = max(nodes, key=key)

Prevention

When it happens

Trigger: x = []; x.append(x); x < x, comparing two mutually-referencing lists, or sorting a list whose elements are deeply nested structures (thousands of levels) with < or sorted().

Common situations: Building cyclic configuration graphs in Starlark (node lists containing node lists) and then sorting or min/max-ing them; deep AST-like data compared directly instead of by a structural key.

Related errors


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