apache/iceberg · error · UnsupportedOperationException
Cannot convert unknown expression: ${expr}
Error message
Cannot convert unknown expression: ${expr} What it means
toIcebergTerm only understands Transform and NamedReference Spark expressions. Any other expression tree node passed in (e.g. a literal, binary comparison, or function call) cannot be converted to an Iceberg term, so this UnsupportedOperationException is thrown.
Source
Thrown at spark/v3.5/spark/src/main/java/org/apache/iceberg/spark/Spark3Util.java:412
return org.apache.iceberg.expressions.Expressions.hour(colName);
case "truncate":
return org.apache.iceberg.expressions.Expressions.truncate(colName, findWidth(transform));
case "zorder":
return new Zorder(
Stream.of(transform.references())
.map(ref -> DOT.join(ref.fieldNames()))
.map(org.apache.iceberg.expressions.Expressions::ref)
.collect(Collectors.toList()));
default:
throw new UnsupportedOperationException("Transform is not supported: " + transform);
}
} else if (expr instanceof NamedReference) {
NamedReference ref = (NamedReference) expr;
return org.apache.iceberg.expressions.Expressions.ref(DOT.join(ref.fieldNames()));
} else {
throw new UnsupportedOperationException("Cannot convert unknown expression: " + expr);
}
}
/**
* Converts Spark transforms into a {@link PartitionSpec}.
*
* @param schema the table schema
* @param partitioning Spark Transforms
* @return a PartitionSpec
*/
public static PartitionSpec toPartitionSpec(Schema schema, Transform[] partitioning) {
if (partitioning == null || partitioning.length == 0) {
return PartitionSpec.unpartitioned();
}
PartitionSpec.Builder builder = PartitionSpec.builderFor(schema);
for (Transform transform : partitioning) {
Preconditions.checkArgument(View on GitHub (pinned to 86d9c8fc54)
Solutions
- Use only plain columns or supported transforms in PARTITIONED BY / sort clauses
- Add a computed column first, then partition on it
- Construct the term programmatically with Expressions.ref/bucket/truncate instead of going through the Spark converter
Example fix
// before PARTITIONED BY (bucket(16, id), ts + INTERVAL 1 DAY) // after PARTITIONED BY (bucket(16, id), days(ts))
Defensive patterns
Strategy: type-guard
Validate before calling
if (!(expr instanceof Transform) && !(expr instanceof NamedReference)) { throw new IllegalArgumentException("Expression must be a Transform or NamedReference"); } Type guard
boolean convertibleToIcebergTerm(Expression e) { return e instanceof Transform || e instanceof NamedReference; } Try / catch
try { Spark3Util.toIcebergTerm(expr); } catch (UnsupportedOperationException e) { if (e.getMessage().contains("Cannot convert unknown expression")) { /* rebuild the expression as a transform/ref */ } else throw e; } Prevention
- Use only column names or supported transforms in PARTITIONED BY clauses
- Compute derived values into real columns instead of inlining expressions
- When building V2 expressions programmatically, verify the node type before converting
When it happens
Trigger: Passing a non-transform, non-column expression into partition/sort conversion paths — e.g. IcebergPartitioningHelpers or CREATE TABLE ... PARTITIONED BY with a predicate or computed expression rather than a plain column or supported transform.
Common situations: PARTITIONED BY clauses containing arithmetic or function calls (PARTITIONED BY (year(col) + 1)); programmatic construction of V2 expressions that aren't NamedReference/Transform.
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
- Cannot add column %s since setting default values in Spark i
- Transform is not supported: ${transform}
- Deleted rows scan task is not supported yet
- Unsupported logical type:
- Unsupported type:
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/231b560cd5a36ba7.
Report an issue: GitHub.