{"record":{"id":"424b662b6a245f16","repo":"apache/druid","slug":"cannot-set-twice","errorCode":null,"errorMessage":"Cannot set twice","messagePattern":"Cannot set twice","errorType":"validation","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"multi-stage-query/src/main/java/org/apache/druid/msq/util/PassthroughAggregator.java","lineNumber":43,"sourceCode":"\nimport javax.annotation.Nullable;\n\npublic class PassthroughAggregator implements Aggregator\n{\n  private final BaseObjectColumnValueSelector<?> selector;\n  private boolean didSet = false;\n  private Object val;\n\n  public PassthroughAggregator(final BaseObjectColumnValueSelector<?> selector)\n  {\n    this.selector = selector;\n  }\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","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/multi-stage-query/src/main/java/org/apache/druid/msq/util/PassthroughAggregator.java#L25-L61","documentation":"PassthroughAggregator is a single-use aggregator that stores exactly one object from its selector. aggregate() enforces the aggregator lifecycle contract (one aggregation per row slot); calling aggregate() twice without resetting violates it and throws ISE.","triggerScenarios":"The query engine invokes aggregate() more than once on the same aggregator instance, typically due to a custom aggregator/engine misuse or a bug in buffer reuse rather than user SQL.","commonSituations":"Custom query engine or extension reusing aggregator buffers incorrectly; testing harness calling aggregate() manually twice.","solutions":["Do not call aggregate() twice on the same instance; reset() the aggregator between uses","If seen in production, check for buffer-reuse bugs in the engine and report/fix the reset path","In tests, call reset() before each aggregate()"],"exampleFix":"// before\nagg.aggregate();\nagg.aggregate(); // ISE\n// after\nagg.aggregate();\nagg.reset();\nagg.aggregate();","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  aggregator.aggregate();\n} catch (ISE e) {\n  if (\"Cannot set twice\".equals(e.getMessage())) { aggregator.reset(); aggregator.aggregate(); }\n  else throw e;\n}","preventionTips":["Call reset() before reusing an aggregator instance","Never manually drive aggregate() twice in tests without reset","Treat aggregator instances as single-use per row/group"],"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"}