apache/iceberg · error · java.lang.UnsupportedOperationException
Cannot convert predicate to SQL: <pred>
Error message
Cannot convert predicate to SQL: <pred>
What it means
Spark3Util's predicate-to-SQL converter (used by DESCRIBE TABLE / expression description) does not have a rendering rule for every Iceberg predicate kind. When it encounters a predicate case it does not handle (anything outside the enumerated cases like EQ, LT, LIKE, IN, etc.), it throws this UnsupportedOperationException.
Source
Thrown at spark/v4.2/spark/src/main/java/org/apache/iceberg/spark/Spark3Util.java:716
return sqlString(pred.term()) + " <= " + sqlString(pred.literal());
case GT:
return sqlString(pred.term()) + " > " + sqlString(pred.literal());
case GT_EQ:
return sqlString(pred.term()) + " >= " + sqlString(pred.literal());
case EQ:
return sqlString(pred.term()) + " = " + sqlString(pred.literal());
case NOT_EQ:
return sqlString(pred.term()) + " != " + sqlString(pred.literal());
case STARTS_WITH:
return sqlString(pred.term()) + " LIKE '" + pred.literal().value() + "%'";
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(", "));View on GitHub (pinned to 86d9c8fc54)
Solutions
- Identify the predicate type printed in the message and express it with a supported predicate kind (e.g. rewrite STARTS_WITH as LIKE 'prefix%' manually).
- Upgrade or downgrade Iceberg's Spark runtime to a version whose converter supports the predicate kind you use.
- Wrap the call and fall back to pred.toString() or skip SQL rendering for unsupported predicates.
- If the predicate kind should be supported, add a case for it in Spark3Util's switch and contribute the fix upstream.
Example fix
// before
filter = new StartsWith("col", "abc");
String sql = Spark3Util.toSqlString(filter); // throws
// after
filter = Expressions.like("col", "abc%");
String sql = Spark3Util.toSqlString(filter); Defensive patterns
Strategy: try-catch
Validate before calling
// Java
if (pred.op() != Expression.Operation.EQ && pred.op() != Expression.Operation.LT /* ...supported ops... */) {
throw new IllegalArgumentException("Unsupported pred for SQL rendering: " + pred.op());
} Type guard
boolean renderable(Expression pred) {
switch (pred.op()) {
case IS_NULL: case NOT_NULL: case LT: case LTE: case GT: case GTE: case EQ:
case NOT_EQ: case IN: case NOT_IN: case LIKE: case NOT_LIKE:
return true;
default:
return false;
}
} Try / catch
try {
String sql = Spark3Util.toSqlString(pred);
} catch (UnsupportedOperationException e) {
logger.warn("Falling back to toString for pred: {}", pred);
sql = pred.toString();
} Prevention
- Stick to documented predicate kinds when building expressions for SQL rendering
- After Iceberg upgrades, test rendering for all predicate kinds you use
- Add a coverage test iterating Expression.Operation values against the renderer
- Bind expressions only for evaluation, keep unbound copies for description
When it happens
Trigger: Calling DescribeExpressionVisitor/Spark3Util SQL rendering on a predicate whose op() falls into the default branch of the switch — e.g. an unsupported or newly added predicate type such as STARTS_WITH, NOT_STARTS_WITH, or a predicate kind from a newer Iceberg expression set passed into a Spark SQL conversion path.
Common situations: Using expression-based SQL description of Iceberg filters after an Iceberg upgrade added new predicate types; passing custom or exotic predicates (e.g. derived predicates from pushed-down filters) into catalog filter rendering.
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 term to SQL: <term>
- 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/142c68b640705b48.
Report an issue: GitHub.