{"record":{"id":"fd031bf4d955bc13","repo":"apache/iceberg","slug":"invalid-operation-for-boundunarypredicate","errorCode":null,"errorMessage":"Invalid operation for BoundUnaryPredicate: ","messagePattern":"Invalid operation for BoundUnaryPredicate: ","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"api/src/main/java/org/apache/iceberg/expressions/BoundUnaryPredicate.java","lineNumber":55,"sourceCode":"\n  @Override\n  public BoundUnaryPredicate<T> asUnaryPredicate() {\n    return this;\n  }\n\n  @Override\n  public boolean test(T value) {\n    switch (op()) {\n      case IS_NULL:\n        return value == null;\n      case NOT_NULL:\n        return value != null;\n      case IS_NAN:\n        return NaNUtil.isNaN(value);\n      case NOT_NAN:\n        return !NaNUtil.isNaN(value);\n      default:\n        throw new IllegalStateException(\"Invalid operation for BoundUnaryPredicate: \" + op());\n    }\n  }\n\n  @Override\n  public boolean isEquivalentTo(Expression other) {\n    if (op() == other.op()) {\n      return term().isEquivalentTo(((BoundUnaryPredicate<?>) other).term());\n    }\n\n    return false;\n  }\n\n  @Override\n  public String toString() {\n    switch (op()) {\n      case IS_NULL:\n        return \"is_null(\" + term() + \")\";\n      case NOT_NULL:","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/api/src/main/java/org/apache/iceberg/expressions/BoundUnaryPredicate.java#L37-L73","documentation":"BoundUnaryPredicate.test() supports only IS_NULL, NOT_NULL, IS_NAN and NOT_NAN operations. Any other operation reaches the default branch and throws IllegalStateException. A BoundUnaryPredicate should never hold other operations, so this signals corrupted construction or an unsafe rewrite.","triggerScenarios":"Evaluating a BoundUnaryPredicate whose op() is something other than the four supported unary operations — only possible through misuse of internal constructors or custom rewrites.","commonSituations":"Custom expression transformation code that mutates operations without matching the predicate class to the operation kind.","solutions":["Only assign unary operations (IS_NULL, NOT_NULL, IS_NAN, NOT_NAN) to BoundUnaryPredicate","Check op() before calling test() in generic evaluation code","Report a bug if this fires with unmodified Iceberg APIs"],"exampleFix":"// before\nunaryPred.test(row.get(pos, type));\n// after\nswitch (unaryPred.op()) {\n  case IS_NULL: case NOT_NULL: case IS_NAN: case NOT_NAN:\n    unaryPred.test(row.get(pos, type));\n    break;\n}\n","handlingStrategy":"validation","validationCode":"Set<Operation> OK = Set.of(Operation.IS_NULL, Operation.NOT_NULL, Operation.IS_NAN, Operation.NOT_NAN); if (!OK.contains(pred.op())) { throw new IllegalArgumentException(\"BoundUnaryPredicate requires a unary op\"); }","typeGuard":"boolean evaluableUnary = pred.op() == Operation.IS_NULL || pred.op() == Operation.NOT_NULL || pred.op() == Operation.IS_NAN || pred.op() == Operation.NOT_NAN;","tryCatchPattern":"try { result = unaryPred.test(value); } catch (IllegalStateException e) { /* corrupt operation: log and fail fast */ }","preventionTips":["Only attach unary operations to BoundUnaryPredicate","Rebuild predicates after rewrites rather than mutating operations","Fail fast on unexpected op() in generic evaluators"],"tags":["expressions","evaluation","internal-invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}