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
- Override notNaN (and isNaN) in your visitor subclass to return an appropriate result for the NaN predicate
- 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
- 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
- Override both isNaN and notNaN whenever you override other double predicates in a custom visitor
- After upgrading Iceberg, run your visitor over an expression set that includes isNaN/notNaN filters
- Prefer extending ExpressionVisitors.CustomOrderExpressionVisitor and implement all predicate methods rather than relying on defaults
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
- In expression is not supported by the visitor
- notIn expression is not supported by the visitor
- startsWith expression is not supported by the visitor
- notStartsWith expression is not supported by the visitor
- Visitor %s does not support non-reference: %s
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/15898ad2e10253c2.
Report an issue: GitHub.