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
- Override handleNonReference(Bound<T> term) in the visitor to handle or conservatively evaluate non-reference terms
- Ensure terms are bound as plain references where possible, or evaluate the predicate against the untransformed column
- 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
- Override handleNonReference in visitors that may see transformed terms (truncate/bucket partition filters)
- Bind expressions carefully and prefer plain column references for evaluator inputs
- Add unit tests visiting filters over partition-transform columns
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
- ${className} does not implement notNaN
- In expression is not supported by the visitor
- notIn expression is not supported by the visitor
- startsWith expression is not supported by the visitor
- notStartsWith expression is not supported by the visitor
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/98bbb91c94bef9c4.
Report an issue: GitHub.