{"record":{"id":"a5314e09dc302551","repo":"apache/druid","slug":"closed","errorCode":null,"errorMessage":"Closed","messagePattern":"Closed","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/frame/processor/manager/AccumulatingProcessorManager.java","lineNumber":54,"sourceCode":"  private final BiFunction<R, T, R> accumulateFn;\n  private R currentResult;\n\n  public AccumulatingProcessorManager(\n      ProcessorManager<T, ?> delegate,\n      R initialResult,\n      BiFunction<R, T, R> accumulateFn\n  )\n  {\n    this.delegate = delegate;\n    this.currentResult = Preconditions.checkNotNull(initialResult, \"initialResult\");\n    this.accumulateFn = accumulateFn;\n  }\n\n  @Override\n  public ListenableFuture<Optional<ProcessorAndCallback<T>>> next()\n  {\n    if (currentResult == null) {\n      throw new ISE(\"Closed\");\n    }\n\n    return FutureUtils.transform(\n        delegate.next(),\n        nextProcessor -> nextProcessor.map(\n            retVal -> new ProcessorAndCallback<>(\n                retVal.processor(),\n                r -> {\n                  currentResult = accumulateFn.apply(currentResult, r);\n                  retVal.onComplete(r);\n                }\n            )\n        )\n    );\n  }\n\n  @Override\n  public R result()","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/frame/processor/manager/AccumulatingProcessorManager.java#L36-L72","documentation":"AccumulatingProcessorManager wraps a delegate ProcessorManager with a lookahead (currentResult). Once the manager has been fully consumed, currentResult is null and next() can no longer be called; invoking it then is an invalid state, so it throws ISE('Closed'). It indicates the iterator-style manager was advanced past its end and used again.","triggerScenarios":"Calling next() after the manager has already returned the final Optional.empty / completed its iteration (currentResult set to null), typically by looping beyond the completion condition.","commonSituations":"Custom processor loops that don't check the returned Optional before calling next() again; reusing a consumed manager instance for a second pass instead of creating a new one.","solutions":["Stop calling next() once it returns Optional.empty()","Create a fresh AccumulatingProcessorManager for each processing pass","Restructure the consumer loop so next() is only invoked while the previous result was present"],"exampleFix":"// before\nwhile (true) {\n  Optional<ProcessorAndCallback<T>> cb = manager.next().get();\n  handle(cb);\n}\n// after\nwhile (manager.next().get().isPresent()) {\n  handle(manager.next().get()); // or store result before handling\n}","handlingStrategy":"try-catch","validationCode":"if (!manager.hasNextState()) { return; } // track consumption yourself before calling next()","typeGuard":"null","tryCatchPattern":"try { Optional<ProcessorAndCallback<T>> r = manager.next().get(); if (!r.isPresent()) { done = true; } } catch (IllegalStateException e) { /* manager consumed: recreate manager */ }","preventionTips":["Stop iterating on Optional.empty()","Never reuse a consumed manager; construct a new one per pass","Encapsulate the loop in one method so termination state is local"],"tags":["iteration","invalid-state","frame-processor"],"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"}