apache/iceberg · error · UnsupportedOperationException

Cannot sanitize unsupported predicate type

Error message

Cannot sanitize unsupported predicate type: ${operation}

What it means

The unbound-predicate sanitizer handles only literal-bearing predicate operations (isNull, notNull, eq, lt, in, etc.); anything reaching the switch's default branch throws this UnsupportedOperationException because its literals cannot be sanitized.

Solutions

  1. Upgrade Iceberg to a version whose sanitizer covers the operation in use
  2. Pre-filter: check pred.op() and leave unsupported predicates unsanitized or handle them explicitly
  3. Sanitize literals yourself and rebuild the predicate with supported ops only

Example fix

// before
String safe = ExpressionUtil.sanitize(ExpressionUtil.predicate("startsWith", "col", "abc"), now, today);
// after
if (pred.op() == Expression.Operation.STARTS_WITH) { /* handle explicitly */ }
else { ExpressionUtil.sanitize(pred, now, today); }
Defensive patterns

Strategy: try-catch

Validate before calling

switch (pred.op()) {
  case IS_NULL: case NOT_NULL: case EQ: case NOT_EQ: case LT: case LT_EQ:
  case GT: case GT_EQ: case IN: case NOT_IN: return true;
  default: return false;
}

Try / catch

try { return ExpressionUtil.sanitize(pred, now, today); }
catch (UnsupportedOperationException e) { return "<unsupported predicate: " + pred.op() + ">"; }

Prevention

When it happens

Trigger: Calling ExpressionUtil.sanitize with an UnboundPredicate whose operation is not in the sanitizer's supported set, e.g. STARTS_WITH, COUNT, or a custom operation.

Common situations: Sanitizing expressions containing STARTS_WITH filters on an Iceberg version whose unbound sanitizer predates support for that op; building predicates with newer operations then passing them to older sanitize code.

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

Appendix: source

Thrown at api/src/main/java/org/apache/iceberg/expressions/ExpressionUtil.java:414

          // unary predicates don't need to be sanitized
          return pred;
        case LT:
        case LT_EQ:
        case GT:
        case GT_EQ:
        case EQ:
        case NOT_EQ:
        case STARTS_WITH:
        case NOT_STARTS_WITH:
          return new UnboundPredicate<>(
              pred.op(), pred.term(), (T) sanitize(pred.literal(), now, today));
        case IN:
        case NOT_IN:
          Iterable<T> iter =
              () -> pred.literals().stream().map(lit -> (T) sanitize(lit, now, today)).iterator();
          return new UnboundPredicate<>(pred.op(), pred.term(), iter);
        default:
          throw new UnsupportedOperationException(
              "Cannot sanitize unsupported predicate type: " + pred.op());
      }
    }
  }

  private static class StringSanitizer extends ExpressionVisitors.ExpressionVisitor<String> {
    private final long nowMicros;
    private final int today;

    private StringSanitizer() {
      long nowMillis = System.currentTimeMillis();
      OffsetDateTime nowDateTime = Instant.ofEpochMilli(nowMillis).atOffset(ZoneOffset.UTC);
      this.nowMicros = nowMillis * 1000;
      this.today = (int) ChronoUnit.DAYS.between(EPOCH, nowDateTime);
    }

    @Override
    public String alwaysTrue() {

View on GitHub (pinned to 86d9c8fc54)