apache/iceberg · error · UnsupportedOperationException

Cannot convert predicate to SQL

Error message

Cannot convert predicate to SQL: ${pred}

What it means

DescribeExpressionVisitor's unbound predicate handler covers the SQL-representable operators (isNull, notNull, comparisons, like/notLike, in/notIn). Any other operator reaching predicate(UnboundPredicate) — e.g. STARTS_ENDS-style or user-defined predicates — has no SQL rendering and throws UnsupportedOperationException naming the offending predicate.

Solutions

  1. Check the predicate operator and only describe expressions with supported operators (isNull, notNull, =, !=, <, <=, >, >=, like, notLike, in, notIn)
  2. Wrap unsupported operators (e.g. convert not(startsWith(...)) to NOT LIKE 'x%') before describing
  3. Upgrade to a Spark/Iceberg version where the operator you use is supported by DescribeExpressionVisitor

Example fix

// before
Expression e = Expressions.not(Expressions.startsWith("col", "abc")); // not(startsWith) not handled
String sql = Spark3Util.describe(schema, e); // throws
// after
String sql = "col NOT LIKE 'abc%'"; // or use an operator the visitor supports
Defensive patterns

Strategy: validation

Validate before calling

Set<Expression.Operation> supported = Set.of(IS_NULL, NOT_NULL, LT, LT_EQ, GT, GT_EQ, EQ, NOT_EQ, LIKE, NOT_LIKE, IN, NOT_IN); if (!supported.contains(expr.op())) { /* use fallback rendering */ }

Type guard

boolean hasSupportedOp(Expression e) { return e instanceof UnboundPredicate && supported.contains(((UnboundPredicate<?>) e).op()); }

Try / catch

try { return Spark3Util.describe(schema, expr); } catch (UnsupportedOperationException e) { return "<unsupported predicate: " + expr + ">"; }

Prevention

When it happens

Trigger: Calling Spark3Util.describe(schema, expr) with an unbound predicate whose operator is not handled by the switch (anything outside the explicitly rendered cases, e.g. custom or newly added predicate kinds), causing the default branch to throw.

Common situations: Using newer Iceberg expression types with an older Spark3Util copy, or describing expressions built programmatically with operators the visitor predates; forwarding user-defined predicates into table description.

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/a07a1ae56aa16675. Report an issue: GitHub.

Appendix: source

Thrown at spark/v4.1/spark/src/main/java/org/apache/iceberg/spark/Spark3Util.java:733

          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)