{"record":{"id":"98bbb91c94bef9c4","repo":"apache/iceberg","slug":"visitor-s-does-not-support-non-reference-s","errorCode":null,"errorMessage":"Visitor %s does not support non-reference: %s","messagePattern":"Visitor (.+?) does not support non-reference: (.+?)","errorType":"validation","errorClass":"ValidationException","httpStatus":null,"severity":"error","filePath":"api/src/main/java/org/apache/iceberg/expressions/ExpressionVisitors.java","lineNumber":141,"sourceCode":"\n    public <T> R notStartsWith(BoundReference<T> ref, Literal<T> lit) {\n      throw new UnsupportedOperationException(\n          \"notStartsWith expression is not supported by the visitor\");\n    }\n\n    /**\n     * Handle a non-reference value in this visitor.\n     *\n     * <p>Visitors that require {@link BoundReference references} and not {@link Bound terms} can\n     * use this method to return a default value for expressions with non-references. The default\n     * implementation will throw a validation exception because the non-reference is not supported.\n     *\n     * @param term a non-reference bound expression\n     * @param <T> a Java return type\n     * @return a return value for the visitor\n     */\n    public <T> R handleNonReference(Bound<T> term) {\n      throw new ValidationException(\"Visitor %s does not support non-reference: %s\", this, term);\n    }\n\n    @Override\n    public <T> R predicate(BoundPredicate<T> pred) {\n      if (!(pred.term() instanceof BoundReference)) {\n        return handleNonReference(pred.term());\n      }\n\n      if (pred.isLiteralPredicate()) {\n        BoundLiteralPredicate<T> literalPred = pred.asLiteralPredicate();\n        switch (pred.op()) {\n          case LT:\n            return lt((BoundReference<T>) pred.term(), literalPred.literal());\n          case LT_EQ:\n            return ltEq((BoundReference<T>) pred.term(), literalPred.literal());\n          case GT:\n            return gt((BoundReference<T>) pred.term(), literalPred.literal());\n          case GT_EQ:","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/api/src/main/java/org/apache/iceberg/expressions/ExpressionVisitors.java#L123-L159","documentation":"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.","triggerScenarios":"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>).","commonSituations":"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.","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"],"exampleFix":"// before\n// visitor lacks handleNonReference -> ValidationException on truncate(col) predicate\n// after\n@Override\npublic <T> R handleNonReference(Bound<T> term) {\n  return ExpressionVisitors.visitEvaluatorFallback(term); // conservative result\n}","handlingStrategy":"try-catch","validationCode":"// check whether any bound predicate term is a non-reference before visiting\nboolean hasNonRefTerm = ExpressionVisitors.visit(boundExpr, new BoundExpressionVisitor<Boolean>() {\n  @Override public <T> Boolean predicate(BoundPredicate<T> pred) {\n    return !(pred.term() instanceof BoundReference);\n  }\n  // remaining methods return false\n});","typeGuard":"static <T> boolean isReferenceTerm(BoundPredicate<T> pred) {\n  return pred.term() instanceof BoundReference;\n}","tryCatchPattern":"try {\n  return ExpressionVisitors.visit(boundExpr, visitor);\n} catch (ValidationException e) {\n  if (e.getMessage().contains(\"does not support non-reference\")) {\n    return conservativeResult();\n  }\n  throw e;\n}","preventionTips":["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"],"tags":["java","expressions","visitor","validation","bound-term"],"backgroundTag":"unsupported-operation","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}