apache/iceberg · error · UnsupportedOperationException

Cannot sanitize bound predicate type

Error message

Cannot sanitize bound predicate type: ${operation}

What it means

The bound-predicate sanitizer in ExpressionUtil only supports bound predicates whose operation takes literals (eq, lt, in, etc.). Operations without bound literal representation (e.g. startsWith on some paths, or count/null ops reached here unexpectedly) fall through to this UnsupportedOperationException.

Solutions

  1. Upgrade Iceberg — newer versions extend the sanitizer to more bound operations
  2. Sanitize the expression before binding (use the unbound predicate sanitizer path) instead of after
  3. Pre-check pred.op() and skip/handle unsupported operations before sanitizing
  4. If sanitizing for logging, fall back to describing the predicate without value redaction for unsupported ops

Example fix

// before
Expression safe = ExpressionUtil.sanitize(boundStartsWithPredicate, now, today);
// after
Expression safe = pred.op() == Expression.Operation.STARTS_WITH
    ? pred // handle or skip unsupported op
    : ExpressionUtil.sanitize(pred, now, today);
Defensive patterns

Strategy: try-catch

Validate before calling

Expression.Operation op = pred.op();
if (op == Expression.Operation.STARTS_WITH || op == Expression.Operation.NOT_STARTS_WITH) {
  throw new IllegalArgumentException("sanitize() does not support bound op: " + op);
}

Try / catch

try { return ExpressionUtil.sanitize(expr, now, today); }
catch (UnsupportedOperationException e) { return expr; /* unsanitized fallback */ }

Prevention

When it happens

Trigger: Running ExpressionUtil.sanitize over a bound predicate whose ExpressionOperation is not one the sanitizer switch handles (e.g. STARTS_WITH/NOT_STARTS_WITH or other unhandled bound ops in that visitor).

Common situations: Sanitizing query strings for logging while using string predicates (startsWith) that the bound sanitizer branch does not cover; version drift where new operations were added but the sanitizer was not updated.

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

Appendix: source

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

    public <T> Expression predicate(BoundPredicate<T> pred) {
      if (pred.isUnaryPredicate()) {
        // unary predicates don't need to be sanitized
        return new UnboundPredicate<>(pred.op(), unbind(pred.term()));
      } else if (pred.isLiteralPredicate()) {
        BoundLiteralPredicate<T> bound = (BoundLiteralPredicate<T>) pred;
        return new UnboundPredicate<>(
            pred.op(), unbind(pred.term()), (T) sanitize(bound.literal(), now, today));
      } else if (pred.isSetPredicate()) {
        BoundSetPredicate<T> bound = (BoundSetPredicate<T>) pred;
        Iterable<T> iter =
            () ->
                bound.literalSet().stream()
                    .map(lit -> (T) sanitize((Literal<?>) lit, now, today))
                    .iterator();
        return new UnboundPredicate<>(pred.op(), unbind(pred.term()), iter);
      }

      throw new UnsupportedOperationException("Cannot sanitize bound predicate type: " + pred.op());
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> Expression predicate(UnboundPredicate<T> pred) {
      switch (pred.op()) {
        case IS_NULL:
        case NOT_NULL:
        case IS_NAN:
        case NOT_NAN:
          // 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:

View on GitHub (pinned to 86d9c8fc54)