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

  1. Negate the unbound expression with Expressions.not(expr) instead of calling negate() on the bound one
  2. Negate before binding: Expressions.not(expr) then Binder.bind(schema, notExpr)
  3. Implement negate() in custom Expression subclasses
  4. 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

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


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