{"record":{"id":"52f71ebe2dcd36f3","repo":"apache/iceberg","slug":"isnan-cannot-be-used-with-a-non-floating-point-col","errorCode":null,"errorMessage":"IsNaN cannot be used with a non-floating-point column","messagePattern":"IsNaN cannot be used with a non-floating-point column","errorType":"validation","errorClass":"ValidationException","httpStatus":null,"severity":"error","filePath":"api/src/main/java/org/apache/iceberg/expressions/UnboundPredicate.java","lineNumber":148,"sourceCode":"            && allAncestorFieldsAreRequired(struct, boundTerm.ref().fieldId())) {\n          return Expressions.alwaysFalse();\n        } else if (boundTerm.type().equals(Types.UnknownType.get())) {\n          return Expressions.alwaysTrue();\n        }\n        return new BoundUnaryPredicate<>(Operation.IS_NULL, boundTerm);\n      case NOT_NULL:\n        if (!boundTerm.producesNull()\n            && allAncestorFieldsAreRequired(struct, boundTerm.ref().fieldId())) {\n          return Expressions.alwaysTrue();\n        } else if (boundTerm.type().equals(Types.UnknownType.get())) {\n          return Expressions.alwaysFalse();\n        }\n        return new BoundUnaryPredicate<>(Operation.NOT_NULL, boundTerm);\n      case IS_NAN:\n        if (floatingType(boundTerm.type().typeId())) {\n          return new BoundUnaryPredicate<>(Operation.IS_NAN, boundTerm);\n        } else {\n          throw new ValidationException(\"IsNaN cannot be used with a non-floating-point column\");\n        }\n      case NOT_NAN:\n        if (floatingType(boundTerm.type().typeId())) {\n          return new BoundUnaryPredicate<>(Operation.NOT_NAN, boundTerm);\n        } else {\n          throw new ValidationException(\"NotNaN cannot be used with a non-floating-point column\");\n        }\n      default:\n        throw new ValidationException(\"Operation must be IS_NULL, NOT_NULL, IS_NAN, or NOT_NAN\");\n    }\n  }\n\n  private boolean allAncestorFieldsAreRequired(StructType struct, int fieldId) {\n    return TypeUtil.ancestorFields(struct.asSchema(), fieldId).stream()\n        .allMatch(Types.NestedField::isRequired);\n  }\n\n  private boolean floatingType(Type.TypeID typeID) {","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/api/src/main/java/org/apache/iceberg/expressions/UnboundPredicate.java#L130-L166","documentation":"Iceberg's IS_NAN predicate is only meaningful for float and double columns. When an UnboundPredicate with Operation.IS_NAN is bound against a struct whose term resolves to a non-floating-point type, binding fails with this ValidationException rather than producing a predicate that can never be true.","triggerScenarios":"Expressions.isNull/isNaN-style filter: e.g. Expressions.isNaN(\"int_col\") or filter(\"isNaN(id)\") where the referenced field is int, long, decimal, string, timestamp, etc., then binding the expression to a table schema.","commonSituations":"User-submitted SQL with IS NAN on integer columns; query builders that emit isNaN generically for numeric fields; schema evolution changing a column from double to a non-float type while old NaN filters remain.","solutions":["Use isNaN only for float/double columns; use isNull for other types","Check the field type in the table schema before building the predicate and choose the operation accordingly","Fix the query/filter to target the correct column or drop the NaN check for non-float columns"],"exampleFix":"// before\nExpression e = Expressions.isNaN(\"int_col\");\n// after\nTypes.NestedField f = schema.findField(\"int_col\");\nExpression e = (f.type().typeId() == Type.TypeID.FLOAT || f.type().typeId() == Type.TypeID.DOUBLE)\n    ? Expressions.isNaN(\"int_col\")\n    : Expressions.isNull(\"int_col\");","handlingStrategy":"validation","validationCode":"Type t = schema.findType(\"col\");\nboolean ok = t instanceof Types.FloatType || t instanceof Types.DoubleType;\nif (!ok) throw new IllegalArgumentException(\"isNaN requires float/double column\");","typeGuard":"static boolean isFloating(Type t) { return t.typeId() == Type.TypeID.FLOAT || t.typeId() == Type.TypeID.DOUBLE; }","tryCatchPattern":"try { Expression bound = Binder.bind(struct, Expressions.isNaN(\"col\"), true); } catch (ValidationException e) { /* fall back to isNull or reject the filter */ }","preventionTips":["Look up the field type before constructing NaN predicates","Use isNaN only for float/double columns; notNull/int columns get isNull","Re-validate stored filters after schema type changes"],"tags":["expressions","predicate","validation","type-mismatch"],"backgroundTag":"type-mismatch","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}