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
- Align Iceberg versions across the classpath so aggregate ops and their toString rendering match (no version skew).
- Only construct Aggregates via the supported factory methods (count, countStar, max, min).
- If extending aggregates is intentional, update the switch in Aggregate.toString to render the new op.
- Catch UnsupportedOperationException around toString-based logging of expressions and fall back to op().toString().
- 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
- Pin a single Iceberg version across modules to avoid op-enum skew.
- Only build aggregates via Aggregate.count/countStar/max/min factories.
- Use ExpressionParser for canonical expression serialization instead of toString.
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
- does not implement eval(StructLike)
- does not implement eval(DataFile)
- does not implement hasValue(DataFile)
- does not implement newAggregator()
- Cannot evaluate
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/00ab0645abce3d53.
Report an issue: GitHub.