apache/iceberg · error · IllegalStateException
Invalid operation for BoundLiteralPredicate:
Error message
Invalid operation for BoundLiteralPredicate:
What it means
BoundLiteralPredicate.test(...) supports only literal-comparison operations (LT, LT_EQ, GT, GT_EQ, EQ, NOT_EQ, STARTS_WITH, NOT_STARTS_WITH). Any other operation (e.g. IS_NULL / IN, which belong to other predicate classes) reaching the switch throws this IllegalStateException.
Source
Thrown at api/src/main/java/org/apache/iceberg/expressions/BoundLiteralPredicate.java:92
switch (op()) {
case LT:
return cmp.compare(value, literal.value()) < 0;
case LT_EQ:
return cmp.compare(value, literal.value()) <= 0;
case GT:
return cmp.compare(value, literal.value()) > 0;
case GT_EQ:
return cmp.compare(value, literal.value()) >= 0;
case EQ:
return cmp.compare(value, literal.value()) == 0;
case NOT_EQ:
return cmp.compare(value, literal.value()) != 0;
case STARTS_WITH:
return String.valueOf(value).startsWith((String) literal.value());
case NOT_STARTS_WITH:
return !String.valueOf(value).startsWith((String) literal.value());
default:
throw new IllegalStateException("Invalid operation for BoundLiteralPredicate: " + op());
}
}
@Override
@SuppressWarnings("unchecked")
public boolean isEquivalentTo(Expression expr) {
if (op() == expr.op()) {
BoundLiteralPredicate<?> other = (BoundLiteralPredicate<?>) expr;
if (term().isEquivalentTo(other.term())) {
// because the term is equivalent, the literal must have the same type, T
Literal<T> otherLiteral = (Literal<T>) other.literal();
Comparator<T> cmp = literal.comparator();
return cmp.compare(literal.value(), otherLiteral.value()) == 0;
}
} else if (expr instanceof BoundLiteralPredicate) {
BoundLiteralPredicate<?> other = (BoundLiteralPredicate<?>) expr;
if (INTEGRAL_TYPES.contains(term().type().typeId()) && term().isEquivalentTo(other.term())) {View on GitHub (pinned to 86d9c8fc54)
Solutions
- Check pred.isLiteralPredicate() and op() before casting/calling test
- Route IS_NULL/NOT_NULL to BoundUnaryPredicate and IN/NOT_IN to BoundSetPredicate
- Use ExpressionVisitors.predicates() dispatch instead of manual casts
Example fix
// before
BoundLiteralPredicate lit = (BoundLiteralPredicate) pred; // may be IS_NULL etc.
boolean r = lit.test(value); // throws for non-literal ops
// after
if (pred.isLiteralPredicate()) {
BoundLiteralPredicate lit = (BoundLiteralPredicate) pred;
boolean r = lit.test(value);
} Defensive patterns
Strategy: type-guard
Validate before calling
if (pred.isLiteralPredicate()) { Set.of(Operation.LT, Operation.LT_EQ, Operation.GT, Operation.GT_EQ, Operation.EQ, Operation.NOT_EQ, Operation.STARTS_WITH, Operation.NOT_STARTS_WITH).contains(pred.op()); } Type guard
if (pred instanceof BoundLiteralPredicate lit && Set.of(Operation.EQ, Operation.LT, Operation.GT).contains(lit.op())) { /* safe to test */ } Try / catch
try { r = lit.test(value); } catch (IllegalStateException e) { if (e.getMessage().startsWith("Invalid operation for BoundLiteralPredicate")) { /* dispatch to unary/set predicate path */ } else { throw e; } } Prevention
- Always narrow via isLiteralPredicate()/isUnaryPredicate()/isSetPredicate()
- Use ExpressionVisitors predicate dispatch
- Never change a predicate's op after binding
When it happens
Trigger: Calling test(value) or matchLiteral on a BoundLiteralPredicate whose op is a non-literal operation, usually after incorrect casting of a generic BoundPredicate to BoundLiteralPredicate without checking isLiteralPredicate().
Common situations: Custom predicate evaluation that casts predicates without narrowing; expression rewriting that swaps operations while keeping the literal predicate class.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- Unsupported aggregate type:
- Not a unary predicate:
- Invalid aggregate:
- Found already bound predicate:
- Found already bound aggregate:
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/146abab13753ae37.
Report an issue: GitHub.