apache/iceberg · error · UnsupportedOperationException

Cannot convert unknown expression:

Error message

Cannot convert unknown expression: 

What it means

Spark3Util.toIcebergTerm converts Spark V2 expression trees (org.apache.spark.sql.connector.expressions.Expression) into Iceberg expressions. It handles Literal, And, Or, Not, NullsOrdering/options and NamedReference; anything else is untranslatable, so it throws UnsupportedOperationException. This guards DESCRIBE TABLE / partition-spec conversion paths from silently producing wrong filters.

Source

Thrown at spark/v4.0/spark/src/main/java/org/apache/iceberg/spark/Spark3Util.java:416

          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

  1. Inspect the failing expression printed in the message; rewrite the query/DDL to use only simple column references, literals and AND/OR/NOT predicates supported by Iceberg
  2. Check whether the expression is a transform/sort-order that should go through Spark3Util.toIcebergTerm's transform path or V2Expressions, not the predicate path
  3. Upgrade or patch Iceberg's Spark module to a version that supports this expression type
  4. If authoring the caller, wrap toIcebergTerm in try/catch for UnsupportedOperationException and degrade to non-pushed filter

Example fix

// before
Expression term = Spark3Util.toIcebergTerm(sparkExpr); // throws on Cast/SortOrder
// after
Expression term;
try {
  term = Spark3Util.toIcebergTerm(sparkExpr);
} catch (UnsupportedOperationException e) {
  term = Expressions.alwaysTrue(); // fall back: evaluate filter in Spark
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Check the expression is one of the supported shapes
boolean supported = expr instanceof Literal || expr instanceof And || expr instanceof Or
    || expr instanceof Not || expr instanceof NamedReference;
if (!supported) throw new IllegalArgumentException("Unsupported Spark expression: " + expr);

Type guard

boolean isConvertible(org.apache.spark.sql.connector.expressions.Expression e) {
  return e instanceof Literal || e instanceof And || e instanceof Or
      || e instanceof Not || e instanceof NamedReference;
}

Try / catch

try {
  Expression term = Spark3Util.toIcebergTerm(expr);
} catch (UnsupportedOperationException e) {
  LOG.warn("Falling back to Spark-side filtering: {}", e.getMessage());
  // keep predicate un-pushed
}

Prevention

When it happens

Trigger: Calling toIcebergTerm with a Spark expression not in the supported set, e.g. a SortOrder, Cast, UserDefinedExpression, or other NamedExpression/transform produced by Spark's connector API instead of a simple literal/comparison/column reference.

Common situations: Running DESCRIBE TABLE EXTENDED or converting a Spark filter/pushdown on a table whose partition transforms or expressions include constructs Iceberg's Spark conversion doesn't support; typically after Spark version changes or custom catalogs supplying exotic expressions.

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


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/6bdd56f12674e265. Report an issue: GitHub.