{"record":{"id":"363afb672efc92ab","repo":"apache/iceberg","slug":"cannot-convert-non-reference-to-parquet-filter-t","errorCode":null,"errorMessage":"Cannot convert non-reference to Parquet filter: <term>","messagePattern":"Cannot convert non-reference to Parquet filter: <term>","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"parquet/src/main/java/org/apache/iceberg/parquet/ParquetFilters.java","lineNumber":114,"sourceCode":"    public FilterPredicate or(FilterPredicate left, FilterPredicate right) {\n      if (left == AlwaysTrue.INSTANCE || right == AlwaysTrue.INSTANCE) {\n        return AlwaysTrue.INSTANCE;\n      } else if (left == AlwaysFalse.INSTANCE) {\n        return right;\n      } else if (right == AlwaysFalse.INSTANCE) {\n        return left;\n      }\n      return FilterApi.or(left, right);\n    }\n\n    protected Expression bind(UnboundPredicate<?> pred) {\n      return pred.bind(schema.asStruct(), caseSensitive);\n    }\n\n    @Override\n    public <T> FilterPredicate predicate(BoundPredicate<T> pred) {\n      if (!(pred.term() instanceof BoundReference)) {\n        throw new UnsupportedOperationException(\n            \"Cannot convert non-reference to Parquet filter: \" + pred.term());\n      }\n\n      Operation op = pred.op();\n      BoundReference<T> ref = (BoundReference<T>) pred.term();\n      String path = schema.idToAlias(ref.fieldId());\n      Literal<T> lit;\n      if (pred.isUnaryPredicate()) {\n        lit = null;\n      } else if (pred.isLiteralPredicate()) {\n        lit = pred.asLiteralPredicate().literal();\n      } else {\n        throw new UnsupportedOperationException(\"Cannot convert to Parquet filter: \" + pred);\n      }\n\n      switch (ref.type().typeId()) {\n        case BOOLEAN:\n          Operators.BooleanColumn col = FilterApi.booleanColumn(path);","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/parquet/src/main/java/org/apache/iceberg/parquet/ParquetFilters.java#L96-L132","documentation":"The Parquet filter translator converts bound Iceberg predicates into parquet-mr FilterPredicates for pushdown. Parquet can only evaluate predicates on direct column references; if a bound predicate's term is not a BoundReference (e.g. a BoundTransform or other composite term), it cannot be pushed down and this UnsupportedOperationException is thrown.","triggerScenarios":"Evaluating an expression containing transform terms (e.g. bucket(...), truncate(...), year(ts)) against Parquet with this translator when the term binds to something other than a plain column reference.","commonSituations":"Queries with partition-transform expressions in filters combined with strict pushdown paths; custom expressions using BoundTransform; engine integration pushing whole bound expression trees into ParquetFilters.","solutions":["Rewrite the filter to reference the raw column and apply transforms after reading, or pre-filter unsupported predicates at planning time.","Only pass predicates whose terms are plain references to predicate()/ParquetFilters.","Upgrade Iceberg — newer code paths remove unsupported predicates instead of throwing."],"exampleFix":"// before\nfilter = Expressions.equal(Expressions.bucket(\"id\", 16), 5); // pushed straight down\n// after\nfilter = Expressions.in(\"id\", <resolved bucket member values>); // push on the reference, transform applied at planning time","handlingStrategy":"validation","validationCode":"// only push down predicates on plain column references\nif (pred instanceof BoundPredicate\n    && !(((BoundPredicate<?>) pred).term() instanceof BoundReference)) {\n  // do not push this predicate down; keep it for post-scan evaluation\n}","typeGuard":"boolean isPushable(BoundPredicate<?> pred) {\n  return pred.term() instanceof BoundReference;\n}","tryCatchPattern":"try {\n  FilterPredicate fp = parquetFilters.predicate(pred);\n} catch (UnsupportedOperationException e) {\n  LOG.debug(\"Predicate not pushable: {}\", e.getMessage());\n  unpushed.add(pred);\n}","preventionTips":["Apply transforms (bucket/truncate/year) at planning time, not inside pushed-down terms.","Check pred.term() instanceof BoundReference before attempting pushdown.","Keep engine expression-binding code in sync with the Parquet translator's capabilities."],"tags":["parquet","pushdown","filter","expression"],"backgroundTag":"unsupported-operation","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}