apache/iceberg · error · java.lang.UnsupportedOperationException
Cannot convert term to SQL: <term>
Error message
Cannot convert term to SQL: <term>
What it means
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.
Source
Thrown at spark/v4.2/spark/src/main/java/org/apache/iceberg/spark/Spark3Util.java:727
case NOT_STARTS_WITH:
return sqlString(pred.term()) + " NOT LIKE '" + pred.literal().value() + "%'";
case IN:
return sqlString(pred.term()) + " IN (" + sqlString(pred.literals()) + ")";
case NOT_IN:
return sqlString(pred.term()) + " NOT IN (" + sqlString(pred.literals()) + ")";
default:
throw new UnsupportedOperationException("Cannot convert predicate to SQL: " + pred);
}
}
private static <T> String sqlString(UnboundTerm<T> term) {
if (term instanceof org.apache.iceberg.expressions.NamedReference) {
return term.ref().name();
} else if (term instanceof UnboundTransform) {
UnboundTransform<?, ?> transform = (UnboundTransform<?, ?>) term;
return transform.transform().toString() + "(" + transform.ref().name() + ")";
} else {
throw new UnsupportedOperationException("Cannot convert term to SQL: " + term);
}
}
private static <T> String sqlString(List<org.apache.iceberg.expressions.Literal<T>> literals) {
return literals.stream()
.map(DescribeExpressionVisitor::sqlString)
.collect(Collectors.joining(", "));
}
private static String sqlString(org.apache.iceberg.expressions.Literal<?> lit) {
if (lit.value() instanceof String) {
return "'" + lit.value() + "'";
} else if (lit.value() instanceof ByteBuffer) {
byte[] bytes = ByteBuffers.toByteArray((ByteBuffer) lit.value());
return "X'" + BaseEncoding.base16().encode(bytes) + "'";
} else {
return lit.value().toString();
}View on GitHub (pinned to 86d9c8fc54)
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.
Example fix
// before
Expression bound = Binder.bind(schema, Expressions.equal("col", 1));
String sql = Spark3Util.toSqlString(bound.term()); // may throw
// after
Expression unbound = Expressions.equal("col", 1);
String sql = Spark3Util.toSqlString(unbound.term()); // NamedReference ok Defensive patterns
Strategy: type-guard
Validate before calling
// Java
if (!(term instanceof NamedReference) && !(term instanceof UnboundTransform)) {
throw new IllegalArgumentException("Term not renderable as SQL: " + term.getClass());
} Type guard
boolean isSqlRenderableTerm(UnboundTerm<?> term) {
return term instanceof NamedReference || term instanceof UnboundTransform;
} Try / catch
try {
sql = Spark3Util.toSqlString(term);
} catch (UnsupportedOperationException e) {
sql = term.toString();
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Describing already-bound expressions instead of unbound parsed expressions; using terms produced by expression binding pipelines inside DESCRIBE / EXPLAIN-style helpers.
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
- Renaming a view is not supported by catalog: ${catalogName}
- Cannot convert predicate to SQL: <pred>
- Cannot retrieve UUID for table <table.name()>
- Unsupported task group for row-based reads: ${partition.task
- Columnar reads are not supported
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/bf917d31e9cd8f99.
Report an issue: GitHub.