apache/iceberg · error · ValidationException
NotNaN cannot be used with a non-floating-point column
Error message
NotNaN cannot be used with a non-floating-point column
What it means
NOT_NAN is the negation of IS_NAN and is likewise only valid on float and double columns. Binding an UnboundPredicate with Operation.NOT_NAN against a term whose bound type is not floating-point throws this ValidationException.
Source
Thrown at api/src/main/java/org/apache/iceberg/expressions/UnboundPredicate.java:154
case NOT_NULL:
if (!boundTerm.producesNull()
&& allAncestorFieldsAreRequired(struct, boundTerm.ref().fieldId())) {
return Expressions.alwaysTrue();
} else if (boundTerm.type().equals(Types.UnknownType.get())) {
return Expressions.alwaysFalse();
}
return new BoundUnaryPredicate<>(Operation.NOT_NULL, boundTerm);
case IS_NAN:
if (floatingType(boundTerm.type().typeId())) {
return new BoundUnaryPredicate<>(Operation.IS_NAN, boundTerm);
} else {
throw new ValidationException("IsNaN cannot be used with a non-floating-point column");
}
case NOT_NAN:
if (floatingType(boundTerm.type().typeId())) {
return new BoundUnaryPredicate<>(Operation.NOT_NAN, boundTerm);
} else {
throw new ValidationException("NotNaN cannot be used with a non-floating-point column");
}
default:
throw new ValidationException("Operation must be IS_NULL, NOT_NULL, IS_NAN, or NOT_NAN");
}
}
private boolean allAncestorFieldsAreRequired(StructType struct, int fieldId) {
return TypeUtil.ancestorFields(struct.asSchema(), fieldId).stream()
.allMatch(Types.NestedField::isRequired);
}
private boolean floatingType(Type.TypeID typeID) {
return Type.TypeID.DOUBLE.equals(typeID) || Type.TypeID.FLOAT.equals(typeID);
}
private Expression bindLiteralOperation(BoundTerm<T> boundTerm) {
if (op() == Operation.STARTS_WITH || op() == Operation.NOT_STARTS_WITH) {
ValidationException.check(View on GitHub (pinned to 86d9c8fc54)
Solutions
- Emit notNaN only for float/double columns; use notNull otherwise
- Validate the referenced field's typeId before constructing the predicate
- Update the filter definition after column type changes
Example fix
// before
Expression e = Expressions.notNaN("long_col");
// after
Type t = schema.findType("long_col");
Expression e = (t.typeId() == Type.TypeID.FLOAT || t.typeId() == Type.TypeID.DOUBLE)
? Expressions.notNaN("long_col")
: Expressions.notNull("long_col"); Defensive patterns
Strategy: validation
Validate before calling
Type t = schema.findType("col");
if (!(t instanceof Types.FloatType) && !(t instanceof Types.DoubleType)) { /* use notNull instead */ } Type guard
static boolean supportsNotNan(Type t) { return t.typeId() == Type.TypeID.FLOAT || t.typeId() == Type.TypeID.DOUBLE; } Try / catch
try { Expression bound = Binder.bind(struct, Expressions.notNaN("col"), true); } catch (ValidationException e) { /* substitute Expressions.notNull("col") */ } Prevention
- Emit notNaN only for float/double columns
- Centralize filter construction so type checks happen in one place
- Update filters when columns change type
When it happens
Trigger: Building Expressions.notNaN("col") where col resolves to int/long/decimal/string/etc., then binding the expression to a Types.StructType (e.g. via table.filter or Binder.bind).
Common situations: Generic filter UIs or SQL translators that emit NOT NaN for any numeric column; stale filters after a column type changed from double to decimal/long.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- IsNaN cannot be used with a non-floating-point column
- Operation must be IS_NULL, NOT_NULL, IS_NAN, or NOT_NAN
- Invalid value for conversion to type %s: %s (%s)
- Visitor %s does not support non-reference: %s
- Cannot get NaN value for type
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/88ab5e2cf21f0fc4.
Report an issue: GitHub.