{"record":{"id":"bf917d31e9cd8f99","repo":"apache/iceberg","slug":"cannot-convert-term-to-sql-term-bf917d","errorCode":null,"errorMessage":"Cannot convert term to SQL: <term>","messagePattern":"Cannot convert term to SQL: <term>","errorType":"exception","errorClass":"java.lang.UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"spark/v4.2/spark/src/main/java/org/apache/iceberg/spark/Spark3Util.java","lineNumber":727,"sourceCode":"        case NOT_STARTS_WITH:\n          return sqlString(pred.term()) + \" NOT LIKE '\" + pred.literal().value() + \"%'\";\n        case IN:\n          return sqlString(pred.term()) + \" IN (\" + sqlString(pred.literals()) + \")\";\n        case NOT_IN:\n          return sqlString(pred.term()) + \" NOT IN (\" + sqlString(pred.literals()) + \")\";\n        default:\n          throw new UnsupportedOperationException(\"Cannot convert predicate to SQL: \" + pred);\n      }\n    }\n\n    private static <T> String sqlString(UnboundTerm<T> term) {\n      if (term instanceof org.apache.iceberg.expressions.NamedReference) {\n        return term.ref().name();\n      } else if (term instanceof UnboundTransform) {\n        UnboundTransform<?, ?> transform = (UnboundTransform<?, ?>) term;\n        return transform.transform().toString() + \"(\" + transform.ref().name() + \")\";\n      } else {\n        throw new UnsupportedOperationException(\"Cannot convert term to SQL: \" + term);\n      }\n    }\n\n    private static <T> String sqlString(List<org.apache.iceberg.expressions.Literal<T>> literals) {\n      return literals.stream()\n          .map(DescribeExpressionVisitor::sqlString)\n          .collect(Collectors.joining(\", \"));\n    }\n\n    private static String sqlString(org.apache.iceberg.expressions.Literal<?> lit) {\n      if (lit.value() instanceof String) {\n        return \"'\" + lit.value() + \"'\";\n      } else if (lit.value() instanceof ByteBuffer) {\n        byte[] bytes = ByteBuffers.toByteArray((ByteBuffer) lit.value());\n        return \"X'\" + BaseEncoding.base16().encode(bytes) + \"'\";\n      } else {\n        return lit.value().toString();\n      }","sourceCodeStart":709,"sourceCodeEnd":745,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v4.2/spark/src/main/java/org/apache/iceberg/spark/Spark3Util.java#L709-L745","documentation":"Spark3Util.sqlString converts an Iceberg UnboundTerm to SQL text, but only recognizes NamedReference and UnboundTransform. Any other term implementation (e.g. bound references or other term types) hits the else branch and throws this UnsupportedOperationException.","triggerScenarios":"Passing a term that is neither a NamedReference nor an UnboundTransform into the SQL rendering path — typically when describing an expression whose term is a BoundReference or another term subclass not handled by the converter.","commonSituations":"Describing already-bound expressions instead of unbound parsed expressions; using terms produced by expression binding pipelines inside DESCRIBE / EXPLAIN-style helpers.","solutions":["Ensure the expression is unbound (parsed) before rendering; bind the term only for evaluation, not for SQL description.","Rewrite the term to a NamedReference or UnboundTransform before calling the renderer.","Catch UnsupportedOperationException and fall back to term.toString().","If a new term type should render, extend sqlString with an instanceof branch upstream."],"exampleFix":"// before\nExpression bound = Binder.bind(schema, Expressions.equal(\"col\", 1));\nString sql = Spark3Util.toSqlString(bound.term()); // may throw\n// after\nExpression unbound = Expressions.equal(\"col\", 1);\nString sql = Spark3Util.toSqlString(unbound.term()); // NamedReference ok","handlingStrategy":"type-guard","validationCode":"// Java\nif (!(term instanceof NamedReference) && !(term instanceof UnboundTransform)) {\n  throw new IllegalArgumentException(\"Term not renderable as SQL: \" + term.getClass());\n}","typeGuard":"boolean isSqlRenderableTerm(UnboundTerm<?> term) {\n  return term instanceof NamedReference || term instanceof UnboundTransform;\n}","tryCatchPattern":"try {\n  sql = Spark3Util.toSqlString(term);\n} catch (UnsupportedOperationException e) {\n  sql = term.toString();\n}","preventionTips":["Render only unbound (parsed) expressions; never pass bound terms to description helpers","Assert term type before calling SQL rendering utilities","Keep expressions built via Expressions factory methods (which yield NamedReference/UnboundTransform)","Add unit tests covering all term types your app produces"],"tags":["spark","iceberg","term-conversion","unsupported-operation"],"backgroundTag":"unsupported-operation","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}