apache/iceberg · error · UnsupportedOperationException

Cannot convert predicate to SQL:

Error message

Cannot convert predicate to SQL: 

What it means

The unbound predicate branch of DescribeExpressionVisitor renders Iceberg predicates as SQL strings for DESCRIBE output. It supports is-null/not-null, eq/neq/lt/lteq/gt/gteq, starts-with/not-starts-with, in/not-in; other operators (e.g. not, count*, equality on terms it can't render) fall to default and throw UnsupportedOperationException.

Solutions

  1. Rewrite the filter using supported operators (e.g. replace NOT(x) with its negated comparison)
  2. Handle unsupported operators in your own rendering instead of DescribeExpressionVisitor
  3. Upgrade Iceberg Spark module if the operator should be supported in newer versions

Example fix

// before
Expressions.not(Expressions.equal("a", 1)) // hits unsupported default in some paths
// after
Expressions.notEqual("a", 1)
Defensive patterns

Strategy: try-catch

Validate before calling

Set<Expression.Operation> supported = Set.of(IS_NULL, NOT_NULL, EQ, NOT_EQ, LT, LT_EQ,
    GT, GT_EQ, STARTS_WITH, NOT_STARTS_WITH, IN, NOT_IN);
if (!supported.contains(pred.op())) throw new IllegalArgumentException("Unsupported op: " + pred.op());

Type guard

boolean isRenderablePredicate(UnboundPredicate<?> p) {
  return switch (p.op()) {
    case IS_NULL, NOT_NULL, EQ, NOT_EQ, LT, LT_EQ, GT, GT_EQ,
         STARTS_WITH, NOT_STARTS_WITH, IN, NOT_IN -> true;
    default -> false;
  };
}

Try / catch

try {
  String sql = visitor.predicate(pred);
} catch (UnsupportedOperationException e) {
  return pred.toString(); // fall back to Iceberg expression string
}

Prevention

When it happens

Trigger: DESCRIBE/EXPLAIN-style SQL rendering of an Iceberg predicate using an operator outside the supported switch, such as predicates containing NOT wrappers reaching this path or new operator types added in later Iceberg specs.

Common situations: Rendering filters with less-common operators through Spark3Util's describe helpers; schema evolution or version skew where newer predicates are passed to an older converter.

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

Appendix: source

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

          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)