{"record":{"id":"9dc1f1964fcc57bc","repo":"apache/iceberg","slug":"cannot-convert-term-to-sql-term","errorCode":null,"errorMessage":"Cannot convert term to SQL: ${term}","messagePattern":"Cannot convert term to SQL: (.+?)","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"spark/v4.1/spark/src/main/java/org/apache/iceberg/spark/Spark3Util.java","lineNumber":744,"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":726,"sourceCodeEnd":762,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v4.1/spark/src/main/java/org/apache/iceberg/spark/Spark3Util.java#L726-L762","documentation":"sqlString(UnboundTerm<T>) can render only a NamedReference (column name) or an UnboundTransform (transform(col)). Any other term type reaching this visitor has no SQL representation and throws UnsupportedOperationException with the term's toString.","triggerScenarios":"Calling Spark3Util.describe(schema, expr) where the expression references a term that is neither a plain column nor an unbound transform — e.g. bound terms, literal-only terms, or custom UnboundTerm implementations.","commonSituations":"Describing expressions containing custom or bound terms; passing terms from residual/leftover filters into describe; using a term type added after this visitor was written.","solutions":["Ensure predicate terms are NamedReference or UnboundTransform before describing","Extract the column/transform yourself and build a new expression with Expressions.transform or plain column names","Skip describing or implement custom rendering for unsupported term types"],"exampleFix":"// before\nString sql = Spark3Util.describe(schema, someCustomTermPredicate); // throws\n// after\nExpression rebuilt = Expressions.equal(\"col\", 42); // plain NamedReference term\nString sql = Spark3Util.describe(schema, rebuilt);","handlingStrategy":"type-guard","validationCode":"if (!(term instanceof NamedReference) && !(term instanceof UnboundTransform)) { throw new IllegalArgumentException(\"Unsupported term: \" + term); }","typeGuard":"boolean isSqlRenderableTerm(Term t) { return t instanceof NamedReference || t instanceof UnboundTransform; }","tryCatchPattern":"try { return Spark3Util.describe(schema, expr); } catch (UnsupportedOperationException e) { log.warn(\"Term not renderable: {}\", e.getMessage()); return null; }","preventionTips":["Build describe-input expressions only from column refs and Expressions.transform","Do not pass bound terms into unbound description APIs","Rebuild custom terms as UnboundTransform before describing"],"tags":["spark","sql-translation","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-23T08:17:48.524Z"}