apache/iceberg · error · ValidationException

Visitor %s does not support non-reference: %s

Error message

Visitor %s does not support non-reference: %s

What it means

BoundPredicate.visit dispatches predicate evaluation to predicate(BoundPredicate), which requires the predicate's term to be a plain BoundReference. When the term is a non-reference bound expression (e.g. a bound transform, cast, or other composed term), the default handleNonReference throws ValidationException naming the visitor and the unsupported term. Visitors that only understand column references must override handleNonReference or the producer must avoid non-reference terms.

Source

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

    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.
     *
     * @param term a non-reference bound expression
     * @param <T> a Java return type
     * @return a return value for the visitor
     */
    public <T> R handleNonReference(Bound<T> term) {
      throw new ValidationException("Visitor %s does not support non-reference: %s", this, term);
    }

    @Override
    public <T> R predicate(BoundPredicate<T> pred) {
      if (!(pred.term() instanceof BoundReference)) {
        return handleNonReference(pred.term());
      }

      if (pred.isLiteralPredicate()) {
        BoundLiteralPredicate<T> literalPred = pred.asLiteralPredicate();
        switch (pred.op()) {
          case LT:
            return lt((BoundReference<T>) pred.term(), literalPred.literal());
          case LT_EQ:
            return ltEq((BoundReference<T>) pred.term(), literalPred.literal());
          case GT:
            return gt((BoundReference<T>) pred.term(), literalPred.literal());
          case GT_EQ:

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Override handleNonReference(Bound<T> term) in the visitor to handle or conservatively evaluate non-reference terms
  2. Ensure terms are bound as plain references where possible, or evaluate the predicate against the untransformed column
  3. Catch ValidationException and fall back to a safe result (e.g. return rows) rather than failing the scan

Example fix

// before
// visitor lacks handleNonReference -> ValidationException on truncate(col) predicate
// after
@Override
public <T> R handleNonReference(Bound<T> term) {
  return ExpressionVisitors.visitEvaluatorFallback(term); // conservative result
}
Defensive patterns

Strategy: try-catch

Validate before calling

// check whether any bound predicate term is a non-reference before visiting
boolean hasNonRefTerm = ExpressionVisitors.visit(boundExpr, new BoundExpressionVisitor<Boolean>() {
  @Override public <T> Boolean predicate(BoundPredicate<T> pred) {
    return !(pred.term() instanceof BoundReference);
  }
  // remaining methods return false
});

Type guard

static <T> boolean isReferenceTerm(BoundPredicate<T> pred) {
  return pred.term() instanceof BoundReference;
}

Try / catch

try {
  return ExpressionVisitors.visit(boundExpr, visitor);
} catch (ValidationException e) {
  if (e.getMessage().contains("does not support non-reference")) {
    return conservativeResult();
  }
  throw e;
}

Prevention

When it happens

Trigger: Visiting a bound predicate whose term is not a BoundReference — for example a predicate bound to a transform like truncate(10, col) or bucket(n, col) — with a visitor that does not override handleNonReference(Bound<T>).

Common situations: Filters on partition transforms (truncate/bucket/year etc.) pushed into evaluators that assume plain column references; custom expression visitors receiving complex bound terms from newer Iceberg versions.

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/98bbb91c94bef9c4. Report an issue: GitHub.