apache/iceberg · error · IllegalArgumentException
No negation for operation: ${operation}
Error message
No negation for operation: ${operation} What it means
Operation.negate() returns the inverse operation (e.g. EQ -> NOT_EQ, IN -> NOT_IN). Some operations have no logical negation, so the default branch throws IllegalArgumentException listing the operation. Only invertible operations can be negated at the operation level.
Source
Thrown at api/src/main/java/org/apache/iceberg/expressions/Expression.java:95
return Operation.GT;
case GT:
return Operation.LT_EQ;
case GT_EQ:
return Operation.LT;
case EQ:
return Operation.NOT_EQ;
case NOT_EQ:
return Operation.EQ;
case IN:
return Operation.NOT_IN;
case NOT_IN:
return Operation.IN;
case STARTS_WITH:
return Operation.NOT_STARTS_WITH;
case NOT_STARTS_WITH:
return Operation.STARTS_WITH;
default:
throw new IllegalArgumentException("No negation for operation: " + this);
}
}
/** Returns the equivalent operation when the left and right operands are exchanged. */
// Allow flipLR as a name because it's a public API
@SuppressWarnings("checkstyle:AbbreviationAsWordInName")
public Operation flipLR() {
switch (this) {
case LT:
return Operation.GT;
case LT_EQ:
return Operation.GT_EQ;
case GT:
return Operation.LT;
case GT_EQ:
return Operation.LT_EQ;
case EQ:
return Operation.EQ;View on GitHub (pinned to 86d9c8fc54)
Solutions
- Check whether the operation has a negation before calling negate() (e.g. switch on op)
- Wrap the expression in Expressions.not(expr) instead of relying on operation-level negation if the op is non-invertible
- Restrict negation to operations like EQ, NOT_EQ, LT, LT_EQ, GT, GT_EQ, IN, NOT_IN, IS_NULL, NOT_NULL, STARTS_WITH, NOT_STARTS_WITH
Example fix
// before Operation negated = op.negate(); // throws for IS_NAN // after Expression negated = Expressions.not(expr); // safe generic negation
Defensive patterns
Strategy: validation
Validate before calling
Set<Operation> negatable = Set.of(EQ, NOT_EQ, LT, LT_EQ, GT, GT_EQ, IN, NOT_IN, IS_NULL, NOT_NULL, STARTS_WITH, NOT_STARTS_WITH); if (!negatable.contains(op)) { /* use Expressions.not(expr) instead */ } Type guard
boolean hasNegation = !Set.of(Operation.IS_NAN, Operation.NOT_NAN).contains(op);
Try / catch
try { negated = op.negate(); } catch (IllegalArgumentException e) { negated = null; /* wrap with Expressions.not instead */ } Prevention
- Prefer Expressions.not(expr) over operation-level negate
- Only negate operations known to have inverses
- Push NOT down only after checking each operation
When it happens
Trigger: Calling Operation.negate() on an operation without an inverse, such as IS_NAN/NOT_NAN or STARTS_WITH-family operations not covered by the switch, typically while building NOT(expr) via ExpressionParser or calling expr.negate()/not(...).
Common situations: Rewriting filters by pushing NOT down into predicates; users negating expressions containing operations that lack operation-level negation.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Invalid operation type: %s
- No left-right flip for operation: ${operation}
- %s cannot be negated
- Invalid distribution mode: %s
- Invalid file format: %s
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/2c72a3a7053dc368.
Report an issue: GitHub.