apache/iceberg · error · UnsupportedOperationException

${className} does not implement notNaN

Error message

${className} does not implement notNaN

What it means

ExpressionVisitors.CustomOrderExpressionVisitor (and its base classes) provide default implementations of isNaN/notNaN that throw UnsupportedOperationException. The library throws this when an expression visitor is asked to evaluate a notNaN (or isNaN) predicate but the concrete visitor subclass has not overridden the method. It signals that the visitor implementation does not cover this newer predicate kind.

Source

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

    }
  }

  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;
    }

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

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Override notNaN (and isNaN) in your visitor subclass to return an appropriate result for the NaN predicate
  2. If the visitor genuinely cannot handle NaN predicates, catch UnsupportedOperationException and fall back to a conservative result (e.g. rows=true) or reject the expression
  3. Upgrade to a visitor/evaluator implementation that supports NaN predicates

Example fix

// before
class MyVisitor<T> extends ExpressionVisitors.CustomOrderExpressionVisitor<T> { /* no notNaN */ }
// after
class MyVisitor<T> extends ExpressionVisitors.CustomOrderExpressionVisitor<T> {
  @Override
  public <F> T notNaN(BoundReference<F> ref) { return handleNaN(ref); }
  @Override
  public <F> T isNaN(BoundReference<F> ref) { return handleNaN(ref); }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// detect NaN predicates in the expression before visiting
boolean hasNaN = new ExpressionVisitors.ExpressionVisitor<Boolean>() {
  @Override public Boolean predicate(UnboundPredicate<?> p) {
    return p.op() == Expression.Operation.IS_NAN || p.op() == Expression.Operation.NOT_NAN;
  }
  @Override public Boolean alwaysTrue() { return false; }
  @Override public Boolean alwaysFalse() { return false; }
  @Override public Boolean not(Boolean r) { return r; }
  @Override public Boolean and(Boolean l, Boolean r) { return l || r; }
  @Override public Boolean or(Boolean l, Boolean r) { return l || r; }
}.visit(expr);

Type guard

boolean supportsNotNaN(ExpressionVisitor<?> v) {
  try { v.getClass().getDeclaredMethod("notNaN", BoundReference.class); return true; }
  catch (NoSuchMethodException e) { return false; }
}

Try / catch

try {
  return visitPredicate(pred, visitor);
} catch (UnsupportedOperationException e) {
  if (e.getMessage().endsWith("does not implement notNaN")) {
    return conservativeResult(); // e.g. return rows
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling ExpressionVisitors.visit(pred, visitor) or pred's accept path where pred is a BoundPredicate from notNaN(col) (or isNaN), and the visitor class in use does not override notNaN(BoundReference). Typically happens after upgrading Iceberg and running an older visitor (e.g. a custom metrics evaluator or rewrite) against expressions that now include NaN checks.

Common situations: Engine integrations (Spark/Flink) or third-party evaluators written against an older visitor API encountering newly added isNaN/notNaN predicates in user filters; custom visitors that only overrode eq/lt/etc.

Related errors


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