apache/iceberg · error · IllegalStateException
Not a unary predicate:
Error message
Not a unary predicate:
What it means
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.
Source
Thrown at api/src/main/java/org/apache/iceberg/expressions/BoundPredicate.java:50
public abstract boolean test(T value);
@Override
public Boolean eval(StructLike struct) {
return test(term().eval(struct));
}
@Override
public BoundReference<?> ref() {
return term().ref();
}
public boolean isUnaryPredicate() {
return false;
}
public BoundUnaryPredicate<T> asUnaryPredicate() {
throw new IllegalStateException("Not a unary predicate: " + this);
}
public boolean isLiteralPredicate() {
return false;
}
public BoundLiteralPredicate<T> asLiteralPredicate() {
throw new IllegalStateException("Not a literal predicate: " + this);
}
public boolean isSetPredicate() {
return false;
}
public BoundSetPredicate<T> asSetPredicate() {
throw new IllegalStateException("Not a set predicate: " + this);
}
}View on GitHub (pinned to 86d9c8fc54)
Solutions
- Call isUnaryPredicate() and only then asUnaryPredicate()
- Use instanceof / pattern narrowing to BoundUnaryPredicate directly
- Dispatch via ExpressionVisitors.predicate visitors instead of manual casting
Example fix
// before
BoundUnaryPredicate<?> unary = pred.asUnaryPredicate(); // throws for literal preds
// after
if (pred.isUnaryPredicate()) {
BoundUnaryPredicate<?> unary = pred.asUnaryPredicate();
} Defensive patterns
Strategy: type-guard
Validate before calling
if (!pred.isUnaryPredicate()) { /* not IS_NULL/NOT_NULL; do not narrow */ } Type guard
if (pred.isUnaryPredicate()) { BoundUnaryPredicate<?> unary = pred.asUnaryPredicate(); } Try / catch
try { unary = pred.asUnaryPredicate(); } catch (IllegalStateException e) { if (e.getMessage().startsWith("Not a unary predicate")) { /* handle as literal/set predicate */ } else { throw e; } } Prevention
- Check isUnaryPredicate() before asUnaryPredicate()
- Prefer instanceof narrowing to BoundUnaryPredicate
- Dispatch predicates through visitors instead of manual casts
When it happens
Trigger: Calling asUnaryPredicate() on a BoundLiteralPredicate or BoundSetPredicate without first checking isUnaryPredicate().
Common situations: Predicate-rewrite code that assumes unary after filtering wrong; pushdown logic casting predicates to extract null checks.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Invalid operation for BoundLiteralPredicate:
- Invalid aggregate:
- Found already bound predicate:
- Found already bound aggregate:
- does not implement eval(StructLike)
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/59aef12d5ce6dfbb.
Report an issue: GitHub.