{"record":{"id":"59aef12d5ce6dfbb","repo":"apache/iceberg","slug":"not-a-unary-predicate","errorCode":null,"errorMessage":"Not a unary predicate: ","messagePattern":"Not a unary predicate: ","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"api/src/main/java/org/apache/iceberg/expressions/BoundPredicate.java","lineNumber":50,"sourceCode":"\n  public abstract boolean test(T value);\n\n  @Override\n  public Boolean eval(StructLike struct) {\n    return test(term().eval(struct));\n  }\n\n  @Override\n  public BoundReference<?> ref() {\n    return term().ref();\n  }\n\n  public boolean isUnaryPredicate() {\n    return false;\n  }\n\n  public BoundUnaryPredicate<T> asUnaryPredicate() {\n    throw new IllegalStateException(\"Not a unary predicate: \" + this);\n  }\n\n  public boolean isLiteralPredicate() {\n    return false;\n  }\n\n  public BoundLiteralPredicate<T> asLiteralPredicate() {\n    throw new IllegalStateException(\"Not a literal predicate: \" + this);\n  }\n\n  public boolean isSetPredicate() {\n    return false;\n  }\n\n  public BoundSetPredicate<T> asSetPredicate() {\n    throw new IllegalStateException(\"Not a set predicate: \" + this);\n  }\n}","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/api/src/main/java/org/apache/iceberg/expressions/BoundPredicate.java#L32-L68","documentation":"BoundPredicate.asUnaryPredicate() narrows a generic bound predicate to BoundUnaryPredicate (IS_NULL/NOT_NULL). If the predicate is not actually unary (e.g. a literal or set predicate), the narrowing is invalid and this IllegalStateException is thrown.","triggerScenarios":"Calling asUnaryPredicate() on a BoundLiteralPredicate or BoundSetPredicate without first checking isUnaryPredicate().","commonSituations":"Predicate-rewrite code that assumes unary after filtering wrong; pushdown logic casting predicates to extract null checks.","solutions":["Call isUnaryPredicate() and only then asUnaryPredicate()","Use instanceof / pattern narrowing to BoundUnaryPredicate directly","Dispatch via ExpressionVisitors.predicate visitors instead of manual casting"],"exampleFix":"// before\nBoundUnaryPredicate<?> unary = pred.asUnaryPredicate(); // throws for literal preds\n// after\nif (pred.isUnaryPredicate()) {\n  BoundUnaryPredicate<?> unary = pred.asUnaryPredicate();\n}","handlingStrategy":"type-guard","validationCode":"if (!pred.isUnaryPredicate()) { /* not IS_NULL/NOT_NULL; do not narrow */ }","typeGuard":"if (pred.isUnaryPredicate()) { BoundUnaryPredicate<?> unary = pred.asUnaryPredicate(); }","tryCatchPattern":"try { unary = pred.asUnaryPredicate(); } catch (IllegalStateException e) { if (e.getMessage().startsWith(\"Not a unary predicate\")) { /* handle as literal/set predicate */ } else { throw e; } }","preventionTips":["Check isUnaryPredicate() before asUnaryPredicate()","Prefer instanceof narrowing to BoundUnaryPredicate","Dispatch predicates through visitors instead of manual casts"],"tags":["expressions","predicates","type-cast"],"backgroundTag":"invalid-argument-value","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T21:17:11.552Z"}