apache/iceberg · error · UnsupportedOperationException

Cannot convert term to SQL

Error message

Cannot convert term to SQL: ${term}

What it means

sqlString(UnboundTerm<T>) can render only a NamedReference (column name) or an UnboundTransform (transform(col)). Any other term type reaching this visitor has no SQL representation and throws UnsupportedOperationException with the term's toString.

Solutions

  1. Ensure predicate terms are NamedReference or UnboundTransform before describing
  2. Extract the column/transform yourself and build a new expression with Expressions.transform or plain column names
  3. Skip describing or implement custom rendering for unsupported term types

Example fix

// before
String sql = Spark3Util.describe(schema, someCustomTermPredicate); // throws
// after
Expression rebuilt = Expressions.equal("col", 42); // plain NamedReference term
String sql = Spark3Util.describe(schema, rebuilt);
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(term instanceof NamedReference) && !(term instanceof UnboundTransform)) { throw new IllegalArgumentException("Unsupported term: " + term); }

Type guard

boolean isSqlRenderableTerm(Term t) { return t instanceof NamedReference || t instanceof UnboundTransform; }

Try / catch

try { return Spark3Util.describe(schema, expr); } catch (UnsupportedOperationException e) { log.warn("Term not renderable: {}", e.getMessage()); return null; }

Prevention

When it happens

Trigger: Calling Spark3Util.describe(schema, expr) where the expression references a term that is neither a plain column nor an unbound transform — e.g. bound terms, literal-only terms, or custom UnboundTerm implementations.

Common situations: Describing expressions containing custom or bound terms; passing terms from residual/leftover filters into describe; using a term type added after this visitor was written.

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

Appendix: source

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

        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)