{"record":{"id":"00ab0645abce3d53","repo":"apache/iceberg","slug":"invalid-aggregate","errorCode":null,"errorMessage":"Invalid aggregate: ","messagePattern":"Invalid aggregate: ","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"api/src/main/java/org/apache/iceberg/expressions/Aggregate.java","lineNumber":57,"sourceCode":"  public C term() {\n    return term;\n  }\n\n  @Override\n  public String toString() {\n    switch (op()) {\n      case COUNT:\n        return \"count(\" + term() + \")\";\n      case COUNT_NULL:\n        return \"count_if(\" + term() + \" is null)\";\n      case COUNT_STAR:\n        return \"count(*)\";\n      case MAX:\n        return \"max(\" + term() + \")\";\n      case MIN:\n        return \"min(\" + term() + \")\";\n      default:\n        throw new UnsupportedOperationException(\"Invalid aggregate: \" + op());\n    }\n  }\n}\n","sourceCodeStart":39,"sourceCodeEnd":61,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/api/src/main/java/org/apache/iceberg/expressions/Aggregate.java#L39-L61","documentation":"Aggregate.toString() maps each known aggregate operator (COUNT, MAX, MIN) to a readable string; any other op value has no rendering, so the default branch throws UnsupportedOperationException with the message 'Invalid aggregate: ' + op(). Reaching this means an aggregate expression holds an operator that toString cannot serialize.","triggerScenarios":"Calling toString() on an Aggregate/BoundAggregate whose op is not COUNT, MAX, MIN — e.g., COUNT_STAR-like or future/foreign operator enums, or reflection/instrumentation constructing an Aggregate with an unexpected Op.","commonSituations":"Logging or printing expression trees that contain aggregate types from a newer Iceberg version read by an older version on the classpath; custom code building Aggregates with synthetic ops; debuggers/IDEs invoking toString while inspecting expressions.","solutions":["Align Iceberg versions across the classpath so aggregate ops and their toString rendering match (no version skew).","Only construct Aggregates via the supported factory methods (count, countStar, max, min).","If extending aggregates is intentional, update the switch in Aggregate.toString to render the new op.","Catch UnsupportedOperationException around toString-based logging of expressions and fall back to op().toString().","Avoid relying on toString for serialization; use ExpressionParser for canonical JSON output."],"exampleFix":"// before\nString s = aggregate.toString(); // throws for unknown op\n// after\nString s;\ntry {\n  s = aggregate.toString();\n} catch (UnsupportedOperationException e) {\n  s = aggregate.op().toString();\n}","handlingStrategy":"try-catch","validationCode":"Op op = aggregate.op();\nif (op != Op.COUNT && op != Op.MAX && op != Op.MIN) {\n  throw new IllegalStateException(\"Aggregate op not renderable: \" + op);\n}","typeGuard":"boolean isRenderableAggregate(Aggregate<?> a) {\n  Op op = a.op();\n  return op == Op.COUNT || op == Op.MAX || op == Op.MIN;\n}","tryCatchPattern":"try {\n  text = aggregate.toString();\n} catch (UnsupportedOperationException e) {\n  text = aggregate.op().toString();\n}","preventionTips":["Pin a single Iceberg version across modules to avoid op-enum skew.","Only build aggregates via Aggregate.count/countStar/max/min factories.","Use ExpressionParser for canonical expression serialization instead of toString."],"tags":["expressions","unsupported-operation","tostring"],"backgroundTag":"invalid-enum-value","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"}