apache/iceberg · error · UnsupportedOperationException

Invalid aggregate:

Error message

Invalid aggregate: 

What it means

Aggregate.toString() maps each known aggregate operator (COUNT, MAX, MIN) to a readable string; any other op value has no rendering, so the default branch throws UnsupportedOperationException with the message 'Invalid aggregate: ' + op(). Reaching this means an aggregate expression holds an operator that toString cannot serialize.

Source

Thrown at api/src/main/java/org/apache/iceberg/expressions/Aggregate.java:57

  public C term() {
    return term;
  }

  @Override
  public String toString() {
    switch (op()) {
      case COUNT:
        return "count(" + term() + ")";
      case COUNT_NULL:
        return "count_if(" + term() + " is null)";
      case COUNT_STAR:
        return "count(*)";
      case MAX:
        return "max(" + term() + ")";
      case MIN:
        return "min(" + term() + ")";
      default:
        throw new UnsupportedOperationException("Invalid aggregate: " + op());
    }
  }
}

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Align Iceberg versions across the classpath so aggregate ops and their toString rendering match (no version skew).
  2. Only construct Aggregates via the supported factory methods (count, countStar, max, min).
  3. If extending aggregates is intentional, update the switch in Aggregate.toString to render the new op.
  4. Catch UnsupportedOperationException around toString-based logging of expressions and fall back to op().toString().
  5. Avoid relying on toString for serialization; use ExpressionParser for canonical JSON output.

Example fix

// before
String s = aggregate.toString(); // throws for unknown op
// after
String s;
try {
  s = aggregate.toString();
} catch (UnsupportedOperationException e) {
  s = aggregate.op().toString();
}
Defensive patterns

Strategy: try-catch

Validate before calling

Op op = aggregate.op();
if (op != Op.COUNT && op != Op.MAX && op != Op.MIN) {
  throw new IllegalStateException("Aggregate op not renderable: " + op);
}

Type guard

boolean isRenderableAggregate(Aggregate<?> a) {
  Op op = a.op();
  return op == Op.COUNT || op == Op.MAX || op == Op.MIN;
}

Try / catch

try {
  text = aggregate.toString();
} catch (UnsupportedOperationException e) {
  text = aggregate.op().toString();
}

Prevention

When it happens

Trigger: Calling toString() on an Aggregate/BoundAggregate whose op is not COUNT, MAX, MIN — e.g., COUNT_STAR-like or future/foreign operator enums, or reflection/instrumentation constructing an Aggregate with an unexpected Op.

Common situations: Logging or printing expression trees that contain aggregate types from a newer Iceberg version read by an older version on the classpath; custom code building Aggregates with synthetic ops; debuggers/IDEs invoking toString while inspecting expressions.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/00ab0645abce3d53. Report an issue: GitHub.