{"record":{"id":"142c68b640705b48","repo":"apache/iceberg","slug":"cannot-convert-predicate-to-sql-pred-142c68","errorCode":null,"errorMessage":"Cannot convert predicate to SQL: <pred>","messagePattern":"Cannot convert predicate to SQL: <pred>","errorType":"exception","errorClass":"java.lang.UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"spark/v4.2/spark/src/main/java/org/apache/iceberg/spark/Spark3Util.java","lineNumber":716,"sourceCode":"          return sqlString(pred.term()) + \" <= \" + sqlString(pred.literal());\n        case GT:\n          return sqlString(pred.term()) + \" > \" + sqlString(pred.literal());\n        case GT_EQ:\n          return sqlString(pred.term()) + \" >= \" + sqlString(pred.literal());\n        case EQ:\n          return sqlString(pred.term()) + \" = \" + sqlString(pred.literal());\n        case NOT_EQ:\n          return sqlString(pred.term()) + \" != \" + sqlString(pred.literal());\n        case STARTS_WITH:\n          return sqlString(pred.term()) + \" LIKE '\" + pred.literal().value() + \"%'\";\n        case NOT_STARTS_WITH:\n          return sqlString(pred.term()) + \" NOT LIKE '\" + pred.literal().value() + \"%'\";\n        case IN:\n          return sqlString(pred.term()) + \" IN (\" + sqlString(pred.literals()) + \")\";\n        case NOT_IN:\n          return sqlString(pred.term()) + \" NOT IN (\" + sqlString(pred.literals()) + \")\";\n        default:\n          throw new UnsupportedOperationException(\"Cannot convert predicate to SQL: \" + pred);\n      }\n    }\n\n    private static <T> String sqlString(UnboundTerm<T> term) {\n      if (term instanceof org.apache.iceberg.expressions.NamedReference) {\n        return term.ref().name();\n      } else if (term instanceof UnboundTransform) {\n        UnboundTransform<?, ?> transform = (UnboundTransform<?, ?>) term;\n        return transform.transform().toString() + \"(\" + transform.ref().name() + \")\";\n      } else {\n        throw new UnsupportedOperationException(\"Cannot convert term to SQL: \" + term);\n      }\n    }\n\n    private static <T> String sqlString(List<org.apache.iceberg.expressions.Literal<T>> literals) {\n      return literals.stream()\n          .map(DescribeExpressionVisitor::sqlString)\n          .collect(Collectors.joining(\", \"));","sourceCodeStart":698,"sourceCodeEnd":734,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v4.2/spark/src/main/java/org/apache/iceberg/spark/Spark3Util.java#L698-L734","documentation":"Spark3Util's predicate-to-SQL converter (used by DESCRIBE TABLE / expression description) does not have a rendering rule for every Iceberg predicate kind. When it encounters a predicate case it does not handle (anything outside the enumerated cases like EQ, LT, LIKE, IN, etc.), it throws this UnsupportedOperationException.","triggerScenarios":"Calling DescribeExpressionVisitor/Spark3Util SQL rendering on a predicate whose op() falls into the default branch of the switch — e.g. an unsupported or newly added predicate type such as STARTS_WITH, NOT_STARTS_WITH, or a predicate kind from a newer Iceberg expression set passed into a Spark SQL conversion path.","commonSituations":"Using expression-based SQL description of Iceberg filters after an Iceberg upgrade added new predicate types; passing custom or exotic predicates (e.g. derived predicates from pushed-down filters) into catalog filter rendering.","solutions":["Identify the predicate type printed in the message and express it with a supported predicate kind (e.g. rewrite STARTS_WITH as LIKE 'prefix%' manually).","Upgrade or downgrade Iceberg's Spark runtime to a version whose converter supports the predicate kind you use.","Wrap the call and fall back to pred.toString() or skip SQL rendering for unsupported predicates.","If the predicate kind should be supported, add a case for it in Spark3Util's switch and contribute the fix upstream."],"exampleFix":"// before\nfilter = new StartsWith(\"col\", \"abc\");\nString sql = Spark3Util.toSqlString(filter); // throws\n// after\nfilter = Expressions.like(\"col\", \"abc%\");\nString sql = Spark3Util.toSqlString(filter);","handlingStrategy":"try-catch","validationCode":"// Java\nif (pred.op() != Expression.Operation.EQ && pred.op() != Expression.Operation.LT /* ...supported ops... */) {\n  throw new IllegalArgumentException(\"Unsupported pred for SQL rendering: \" + pred.op());\n}","typeGuard":"boolean renderable(Expression pred) {\n  switch (pred.op()) {\n    case IS_NULL: case NOT_NULL: case LT: case LTE: case GT: case GTE: case EQ:\n    case NOT_EQ: case IN: case NOT_IN: case LIKE: case NOT_LIKE:\n      return true;\n    default:\n      return false;\n  }\n}","tryCatchPattern":"try {\n  String sql = Spark3Util.toSqlString(pred);\n} catch (UnsupportedOperationException e) {\n  logger.warn(\"Falling back to toString for pred: {}\", pred);\n  sql = pred.toString();\n}","preventionTips":["Stick to documented predicate kinds when building expressions for SQL rendering","After Iceberg upgrades, test rendering for all predicate kinds you use","Add a coverage test iterating Expression.Operation values against the renderer","Bind expressions only for evaluation, keep unbound copies for description"],"tags":["spark","iceberg","predicate-conversion","unsupported-operation"],"backgroundTag":"unsupported-operation","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}