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

  1. Ensure the expression is unbound (parsed) before rendering; bind the term only for evaluation, not for SQL description.
  2. Rewrite the term to a NamedReference or UnboundTransform before calling the renderer.
  3. Catch UnsupportedOperationException and fall back to term.toString().
  4. 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

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


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