apache/iceberg · error · UnsupportedOperationException

does not implement isNaN

Error message

${className} does not implement isNaN

What it means

BoundExpressionVisitor.isNaN has no default no-op; the base class throws naming the visitor class that failed to implement it, forcing subclasses to consciously handle NaN checks (important for float/double columns).

Solutions

  1. Override isNaN(BoundReference) (and notNaN) in your visitor subclass
  2. Extend a higher-level visitor (e.g. ExpressionVisitors.CustomOrderExpressionVisitor or evaluator visitors) that already implements NaN handling
  3. Recompile/refresh subclasses after upgrading Iceberg so newly added required methods are implemented

Example fix

// before
class MyVisitor<R> extends ExpressionVisitors.BoundExpressionVisitor<R> {
  @Override public <T> R isNull(BoundReference<T> ref) { return handleNull(ref); }
}
// after
class MyVisitor<R> extends ExpressionVisitors.BoundExpressionVisitor<R> {
  @Override public <T> R isNull(BoundReference<T> ref) { return handleNull(ref); }
  @Override public <T> R isNaN(BoundReference<T> ref) { return handleNaN(ref); }
  @Override public <T> R notNaN(BoundReference<T> ref) { return handleNotNaN(ref); }
}
Defensive patterns

Strategy: validation

Validate before calling

Set<java.lang.reflect.Method> m = Set.of(visitor.getClass().getMethods());
if (!m.contains(visitor.getClass().getMethod("isNaN", BoundReference.class))) {
  throw new IllegalArgumentException("visitor must override isNaN");
}

Try / catch

try { return visitPredicate(pred, visitor); }
catch (UnsupportedOperationException e) { throw new IllegalStateException("Visitor " + visitor.getClass() + " must implement isNaN/notNaN", e); }

Prevention

When it happens

Trigger: Evaluating or visiting an isNaN(floatCol) predicate with a BoundExpressionVisitor subclass that overrides other predicate methods but not isNaN — typically during Bind→visit of row filters containing NaN checks.

Common situations: Custom visitors written before isNaN was added to the API (older subclass compiled against/behaving like the older base); filter visitors that handled isNull/notNull but forgot NaN cases on floating-point columns.

Related errors


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

Appendix: source

Thrown at api/src/main/java/org/apache/iceberg/expressions/ExpressionVisitors.java:78

      throw new UnsupportedOperationException("Cannot visit aggregate expression");
    }

    public <T> R aggregate(UnboundAggregate<T> agg) {
      throw new UnsupportedOperationException("Cannot visit aggregate expression");
    }
  }

  public abstract static class BoundExpressionVisitor<R> extends ExpressionVisitor<R> {
    public <T> R isNull(BoundReference<T> ref) {
      return null;
    }

    public <T> R notNull(BoundReference<T> ref) {
      return null;
    }

    public <T> R isNaN(BoundReference<T> ref) {
      throw new UnsupportedOperationException(
          this.getClass().getName() + " does not implement isNaN");
    }

    public <T> R notNaN(BoundReference<T> ref) {
      throw new UnsupportedOperationException(
          this.getClass().getName() + " does not implement notNaN");
    }

    public <T> R lt(BoundReference<T> ref, Literal<T> lit) {
      return null;
    }

    public <T> R ltEq(BoundReference<T> ref, Literal<T> lit) {
      return null;
    }

    public <T> R gt(BoundReference<T> ref, Literal<T> lit) {
      return null;

View on GitHub (pinned to 86d9c8fc54)