apache/iceberg · error · UnsupportedOperationException

notIn expression is not supported by the visitor

Error message

notIn expression is not supported by the visitor

What it means

The base visitor's default notIn(BoundReference, Set) throws UnsupportedOperationException, marking NOT IN predicates as unsupported unless the visitor opts in. Thrown when visiting an expression containing notIn without an override.

Source

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

    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.
     *
     * <p>Visitors that require {@link BoundReference references} and not {@link Bound terms} can
     * use this method to return a default value for expressions with non-references. The default
     * implementation will throw a validation exception because the non-reference is not supported.

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Override notIn(BoundReference<T>, Set<T>) in the visitor (typically as NOT of the in() result)
  2. Rewrite the expression (e.g. via Expressions.rewriteNot) so notIn is decomposed into supported predicates
  3. Upgrade the evaluator implementation to one supporting notIn

Example fix

// before
// no override -> default throws
// after
@Override
public <T> R notIn(BoundReference<T> ref, Set<T> literalSet) {
  return not(inResult(ref, literalSet));
}
Defensive patterns

Strategy: try-catch

Validate before calling

boolean hasNotIn = exprOpScan(expr, op -> op == Expression.Operation.NOT_IN);

Type guard

static boolean isNotInPredicate(Predicate p) { return p.op() == Expression.Operation.NOT_IN; }

Try / catch

try {
  return visitor.notIn(ref, literalSet);
} catch (UnsupportedOperationException e) {
  if (e.getMessage().contains("notIn expression is not supported")) {
    return not(visitor.in(ref, literalSet));
  }
  throw e;
}

Prevention

When it happens

Trigger: Visiting an expression containing notIn(col, values) (e.g. filter "col NOT IN (...)") with a visitor that does not override notIn(BoundReference<T>, Set<T>).

Common situations: Custom predicate translators or evaluators built before notIn was added to the visitor API encountering newer filters from engines.

Related errors


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