apache/iceberg · error · IllegalArgumentException

No left-right flip for operation: ${operation}

Error message

No left-right flip for operation: ${operation}

What it means

Operation.flipLR() returns the equivalent operation with left/right operands exchanged (LT -> GT, LT_EQ -> GT_EQ, EQ stays EQ, AND/OR stay the same). Operations without a meaningful flip throw IllegalArgumentException. It only makes sense for binary comparison operations.

Source

Thrown at api/src/main/java/org/apache/iceberg/expressions/Expression.java:121

      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;
        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.

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Only call flipLR() on comparison operations (EQ, NOT_EQ, LT, LT_EQ, GT, GT_EQ); symmetric ops AND/OR also support it
  2. Check op kind (isPredicate/isComparison) before flipping
  3. Skip or handle other operations explicitly in the rewriter

Example fix

// before
Operation flipped = op.flipLR(); // throws for IS_NULL
// after
if (op == Operation.EQ || op == Operation.NOT_EQ || op == Operation.LT ||
    op == Operation.LT_EQ || op == Operation.GT || op == Operation.GT_EQ ||
    op == Operation.AND || op == Operation.OR) {
  Operation flipped = op.flipLR();
}
Defensive patterns

Strategy: validation

Validate before calling

EnumSet<Operation> flippable = EnumSet.of(EQ, NOT_EQ, LT, LT_EQ, GT, GT_EQ, AND, OR); if (!flippable.contains(op)) { /* skip flip */ }

Type guard

boolean canFlip = flippable.contains(op);

Try / catch

try { flipped = op.flipLR(); } catch (IllegalArgumentException e) { /* keep original operand order */ }

Prevention

When it happens

Trigger: Calling Operation.flipLR() on a non-comparison operation such as IS_NULL, IN, AND on non-symmetric context, or any unary/set operation, when normalizing expressions (e.g. moving a literal to the other side of a predicate).

Common situations: Expression normalization/optimization code that rewrites 'literal op column' into 'column flippedOp literal' but encounters a non-comparison operation.

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/9928df54c269aea3. Report an issue: GitHub.