apache/iceberg · error · UnsupportedOperationException

In expression is not supported by the visitor

Error message

In expression is not supported by the visitor

What it means

The base expression visitor's default in(BoundReference, Set) throws UnsupportedOperationException because IN predicates are optional for visitors. It is thrown when a visitor that has not overridden the IN case encounters an in predicate. Visitors must opt in by overriding this method.

Source

Thrown at api/src/main/java/org/apache/iceberg/expressions/ExpressionVisitors.java:112

    public <T> R gt(BoundReference<T> ref, Literal<T> lit) {
      return null;
    }

    public <T> R gtEq(BoundReference<T> ref, Literal<T> lit) {
      return null;
    }

    public <T> R eq(BoundReference<T> ref, Literal<T> lit) {
      return null;
    }

    public <T> R notEq(BoundReference<T> ref, Literal<T> lit) {
      return null;
    }

    public <T> R in(BoundReference<T> ref, Set<T> literalSet) {
      throw new UnsupportedOperationException("In expression is not supported by the visitor");
    }

    public <T> R notIn(BoundReference<T> ref, Set<T> literalSet) {
      throw new UnsupportedOperationException("notIn expression is not supported by the visitor");
    }

    public <T> R startsWith(BoundReference<T> ref, Literal<T> lit) {
      throw new UnsupportedOperationException(
          "startsWith expression is not supported by the visitor");
    }

    public <T> R notStartsWith(BoundReference<T> ref, Literal<T> lit) {
      throw new UnsupportedOperationException(
          "notStartsWith expression is not supported by the visitor");
    }

    /**
     * Handle a non-reference value in this visitor.

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Override in(BoundReference<T> ref, Set<T> literalSet) in the visitor to translate/evaluate the IN predicate (often rewritten as a chain of OR equalities)
  2. Alternatively rewrite the expression with ExpressionUtil or rewriteNot/in expansion before visiting if the visitor only supports eq
  3. Update to a newer version of the evaluator/visitor implementation that supports IN

Example fix

// before
// visitor without in() hits default throw
// after
@Override
public <T> R in(BoundReference<T> ref, Set<T> literalSet) {
  return literalSet.stream().map(v -> eqResult(ref, v)).reduce(or, combine); // or direct IN handling
}
Defensive patterns

Strategy: try-catch

Validate before calling

// detect IN predicates before visiting
boolean hasIn = ExpressionVisitors.visit(expr, new ExpressionVisitors.ExpressionVisitor<Boolean>() {
  @Override public Boolean predicate(UnboundPredicate<?> p) { return p.op() == Expression.Operation.IN; }
  @Override public Boolean alwaysTrue() { return false; }
  @Override public Boolean alwaysFalse() { return false; }
  @Override public Boolean not(Boolean r) { return r; }
  @Override public Boolean and(Boolean l, Boolean r) { return l || r; }
  @Override public Boolean or(Boolean l, Boolean r) { return l || r; }
});

Type guard

static boolean isInPredicate(Predicate p) { return p.op() == Expression.Operation.IN; }

Try / catch

try {
  return visitor.in(ref, literalSet);
} catch (UnsupportedOperationException e) {
  if (e.getMessage().contains("In expression is not supported")) {
    return expandInAsOr(ref, literalSet); // rewrite to OR of eq
  }
  throw e;
}

Prevention

When it happens

Trigger: Visiting an expression containing in(col, values) (e.g. from filter "col IN (...)") with a visitor class that does not override in(BoundReference<T>, Set<T>), such as a custom ExpressionVisitor base or an older evaluator predating IN support.

Common situations: Custom scan planners or predicate translators written before Iceberg added IN predicates; passing user SQL filters with IN clauses through an old visitor implementation.

Related errors


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