{"record":{"id":"0cf573e56e8e796b","repo":"apache/druid","slug":"cannot-call-get-before-aggregate","errorCode":null,"errorMessage":"Cannot call get() before aggregate()","messagePattern":"Cannot call get\\(\\) before aggregate\\(\\)","errorType":"validation","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"multi-stage-query/src/main/java/org/apache/druid/msq/util/PassthroughAggregator.java","lineNumber":55,"sourceCode":"  }\n\n  @Override\n  public void aggregate()\n  {\n    if (didSet) {\n      throw new ISE(\"Cannot set twice\");\n    }\n\n    val = selector.getObject();\n    didSet = true;\n  }\n\n  @Nullable\n  @Override\n  public Object get()\n  {\n    if (!didSet) {\n      throw new ISE(\"Cannot call get() before aggregate()\");\n    }\n\n    return val;\n  }\n\n  @Override\n  public float getFloat()\n  {\n    throw new UnsupportedOperationException();\n  }\n\n  @Override\n  public long getLong()\n  {\n    throw new UnsupportedOperationException();\n  }\n\n  @Override","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/multi-stage-query/src/main/java/org/apache/druid/msq/util/PassthroughAggregator.java#L37-L73","documentation":"PassthroughAggregator.get() is a state guard: the aggregator buffers the selected object in aggregate() (setting didSet), and get() before any aggregate() call would return an uninitialized value, so it throws ISE. Indicates the aggregation framework finalized/read the aggregator out of order.","triggerScenarios":"Query engine or test code calls get()/getFloat()/etc. before invoking aggregate() at least once on the aggregator instance.","commonSituations":"Custom aggregation code or a unit test retrieving the result of an aggregator that never aggregated; engine bug where the aggregator was never driven.","solutions":["Report as a bug with the query and aggregator configuration; the framework should always call aggregate() before get().","Check whether custom aggregator code or an unusual query shape (e.g. empty result rows) reaches get() before aggregation."],"exampleFix":"// before\nObject v = agg.get(); // ISE if never aggregated\n// after\nif (agg instanceof PassthroughAggregator) { agg.aggregate(); }\nObject v = agg.get();","handlingStrategy":"type-guard","validationCode":"// aggregate at least once before reading\nif (rows.isEmpty()) { throw new IllegalStateException(\"No rows; aggregator never aggregated\"); }","typeGuard":"// can only be checked post-aggregation; ensure lifecycle\nboolean canGet(PassthroughAggregator a) { a.aggregate(); return true; }","tryCatchPattern":"try {\n  Object v = aggregator.get();\n} catch (ISE e) {\n  if (e.getMessage().contains(\"before aggregate\")) { /* aggregate first or treat as no data */ }\n  else throw e;\n}","preventionTips":["Always call aggregate() (driven by at least one row) before get()","In tests, initialize the selector and call aggregate() in setup","Handle empty-input groups explicitly"],"tags":["aggregator","msq","lifecycle"],"backgroundTag":"invalid-state-transition","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}