apache/iceberg · error · UnsupportedOperationException
%s cannot be negated
Error message
%s cannot be negated
What it means
Expression.negate() is a default method on the Expression interface returning the negation of the expression. Not every concrete Expression implements it (e.g. some aggregate or bound expressions), so the default body throws UnsupportedOperationException naming the expression. Expressions built via Expressions.not() avoid this by wrapping in Not expressions.
Source
Thrown at api/src/main/java/org/apache/iceberg/expressions/Expression.java:131
return Operation.EQ;
case NOT_EQ:
return Operation.NOT_EQ;
case AND:
return Operation.AND;
case OR:
return Operation.OR;
default:
throw new IllegalArgumentException("No left-right flip for operation: " + this);
}
}
}
/** Returns the operation for an expression node. */
Operation op();
/** Returns the negation of this expression, equivalent to not(this). */
default Expression negate() {
throw new UnsupportedOperationException(String.format("%s cannot be negated", this));
}
/**
* Returns whether this expression will accept the same values as another.
*
* <p>If this returns true, the expressions are guaranteed to return the same evaluation for the
* same input. However, if this returns false the expressions may return the same evaluation for
* the same input. That is, expressions may be equivalent even if this returns false.
*
* <p>For best results, rewrite not and bind expressions before calling this method.
*
* @param other another expression
* @return true if the expressions are equivalent
*/
default boolean isEquivalentTo(Expression other) {
// only bound predicates can be equivalent
return false;
}View on GitHub (pinned to 86d9c8fc54)
Solutions
- Negate the unbound expression with Expressions.not(expr) instead of calling negate() on the bound one
- Negate before binding: Expressions.not(expr) then Binder.bind(schema, notExpr)
- Implement negate() in custom Expression subclasses
- Check instanceof BoundExpression and unwrap to the original unbound expression first
Example fix
// before Expression notExpr = Binder.bind(schema, expr).negate(); // throws // after Expression notExpr = Binder.bind(schema, Expressions.not(expr));
Defensive patterns
Strategy: fallback
Validate before calling
if (expr instanceof BoundExpression) { /* negate the unbound original instead */ } Type guard
function negatable(e) { return !(e instanceof org.apache.iceberg.expressions.BoundExpression); } Try / catch
try { notExpr = expr.negate(); } catch (UnsupportedOperationException e) { notExpr = Expressions.not(((BoundExpression) expr).originalExpr()); } Prevention
- Negate expressions before binding, using Expressions.not()
- Avoid calling negate() on bound expressions and aggregates
- Implement negate() in any custom Expression subclass
When it happens
Trigger: Calling negate() directly on an Expression implementation that does not override it — e.g. bound expressions, aggregates, or custom Expression classes — while trying to invert a filter.
Common situations: Inverting user filters for pushdown or query rewriting; calling negate() on BoundExpression (bind-then-negate pattern) rather than negating before binding.
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 aggregate:
- does not implement eval(StructLike)
- does not implement eval(DataFile)
- does not implement hasValue(DataFile)
- does not implement newAggregator()
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/7213c0e0509d0ed4.
Report an issue: GitHub.