{"record":{"id":"d99afe88fdb5c291","repo":"apache/iceberg","slug":"cannot-sanitize-unsupported-predicate-type-oper","errorCode":null,"errorMessage":"Cannot sanitize unsupported predicate type: ${operation}","messagePattern":"Cannot sanitize unsupported predicate type: (.+?)","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"api/src/main/java/org/apache/iceberg/expressions/ExpressionUtil.java","lineNumber":414,"sourceCode":"          // unary predicates don't need to be sanitized\n          return pred;\n        case LT:\n        case LT_EQ:\n        case GT:\n        case GT_EQ:\n        case EQ:\n        case NOT_EQ:\n        case STARTS_WITH:\n        case NOT_STARTS_WITH:\n          return new UnboundPredicate<>(\n              pred.op(), pred.term(), (T) sanitize(pred.literal(), now, today));\n        case IN:\n        case NOT_IN:\n          Iterable<T> iter =\n              () -> pred.literals().stream().map(lit -> (T) sanitize(lit, now, today)).iterator();\n          return new UnboundPredicate<>(pred.op(), pred.term(), iter);\n        default:\n          throw new UnsupportedOperationException(\n              \"Cannot sanitize unsupported predicate type: \" + pred.op());\n      }\n    }\n  }\n\n  private static class StringSanitizer extends ExpressionVisitors.ExpressionVisitor<String> {\n    private final long nowMicros;\n    private final int today;\n\n    private StringSanitizer() {\n      long nowMillis = System.currentTimeMillis();\n      OffsetDateTime nowDateTime = Instant.ofEpochMilli(nowMillis).atOffset(ZoneOffset.UTC);\n      this.nowMicros = nowMillis * 1000;\n      this.today = (int) ChronoUnit.DAYS.between(EPOCH, nowDateTime);\n    }\n\n    @Override\n    public String alwaysTrue() {","sourceCodeStart":396,"sourceCodeEnd":432,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/api/src/main/java/org/apache/iceberg/expressions/ExpressionUtil.java#L396-L432","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Upgrade Iceberg to a version whose sanitizer covers the operation in use","Pre-filter: check pred.op() and leave unsupported predicates unsanitized or handle them explicitly","Sanitize literals yourself and rebuild the predicate with supported ops only"],"exampleFix":"// before\nString safe = ExpressionUtil.sanitize(ExpressionUtil.predicate(\"startsWith\", \"col\", \"abc\"), now, today);\n// after\nif (pred.op() == Expression.Operation.STARTS_WITH) { /* handle explicitly */ }\nelse { ExpressionUtil.sanitize(pred, now, today); }","handlingStrategy":"try-catch","validationCode":"switch (pred.op()) {\n  case IS_NULL: case NOT_NULL: case EQ: case NOT_EQ: case LT: case LT_EQ:\n  case GT: case GT_EQ: case IN: case NOT_IN: return true;\n  default: return false;\n}","typeGuard":null,"tryCatchPattern":"try { return ExpressionUtil.sanitize(pred, now, today); }\ncatch (UnsupportedOperationException e) { return \"<unsupported predicate: \" + pred.op() + \">\"; }","preventionTips":["Check pred.op() before sanitizing unbound predicates","Keep Iceberg versions current for new operations","Sanitize literals manually for unsupported ops"],"tags":["expressions","sanitizer"],"backgroundTag":"unsupported-operation","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}