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

  1. Call isUnaryPredicate() and only then asUnaryPredicate()
  2. Use instanceof / pattern narrowing to BoundUnaryPredicate directly
  3. 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

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


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/59aef12d5ce6dfbb. Report an issue: GitHub.