apache/iceberg · error · UnsupportedOperationException

does not implement newAggregator()

Error message

 does not implement newAggregator()

What it means

newAggregator() creates a streaming Aggregator instance for incremental evaluation; the BoundAggregate base class stub always throws UnsupportedOperationException. Subclasses that do not support row/streaming aggregation trigger this error.

Source

Thrown at api/src/main/java/org/apache/iceberg/expressions/BoundAggregate.java:50

  @Override
  public C eval(StructLike struct) {
    throw new UnsupportedOperationException(
        this.getClass().getName() + " does not implement eval(StructLike)");
  }

  C eval(DataFile file) {
    throw new UnsupportedOperationException(
        this.getClass().getName() + " does not implement eval(DataFile)");
  }

  boolean hasValue(DataFile file) {
    throw new UnsupportedOperationException(
        this.getClass().getName() + " does not implement hasValue(DataFile)");
  }

  Aggregator<C> newAggregator() {
    throw new UnsupportedOperationException(
        this.getClass().getName() + " does not implement newAggregator()");
  }

  boolean containsNan(DataFile file, int fieldId) {
    Long nanCount = safeGet(file.nanValueCounts(), fieldId);
    return nanCount != null && nanCount > 0;
  }

  @Override
  public BoundReference<?> ref() {
    return term().ref();
  }

  public Type type() {
    if (op() == Operation.COUNT || op() == Operation.COUNT_STAR || op() == Operation.COUNT_NULL) {
      return Types.LongType.get();
    } else {
      return term().type();

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Use only COUNT/COUNT_IF/MAX/MIN aggregates with streaming Aggregator paths
  2. Route file-level evaluation through AggregateEvaluator instead
  3. Override newAggregator() in custom BoundAggregate implementations
  4. Check op() before requesting an Aggregator

Example fix

// before
Aggregator<?> aggregator = agg.newAggregator(); // throws
// after
switch (agg.op()) {
  case COUNT: case MAX: case MIN: aggregator = agg.newAggregator(); break;
  default: /* use file-metrics evaluation instead */ break;
}
Defensive patterns

Strategy: type-guard

Validate before calling

boolean streamable = Set.of(Operation.COUNT, Operation.MAX, Operation.MIN).contains(agg.op());

Type guard

boolean streamable = agg instanceof BoundCount || agg instanceof BoundMax || agg instanceof BoundMin;

Try / catch

try { aggregator = agg.newAggregator(); } catch (UnsupportedOperationException e) { /* use file-metrics path instead */ }

Prevention

When it happens

Trigger: Requesting an Aggregator from a BoundAggregate subclass that only supports file-metrics evaluation, e.g. streaming aggregation over rows of an unsupported aggregate type.

Common situations: Custom aggregation pipelines that assume every bound aggregate can aggregate rows incrementally; running Expression aggregation paths on aggregates designed only for metrics pruning.

Related errors


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