{"record":{"id":"ded0f2b5a39bdc4c","repo":"apache/druid","slug":"no-futures-set","errorCode":null,"errorMessage":"No futures set","messagePattern":"No futures set","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/frame/processor/ReturnOrAwait.java","lineNumber":167,"sourceCode":"  /**\n   * The set of channels the processors wants to wait for. Valid if {@link #isAwait()} is true.\n   *\n   * Numbers in this set correspond to positions in the {@link FrameProcessor#inputChannels()} list.\n   */\n  public IntSet awaitableChannels()\n  {\n    if (!hasAwaitableChannels()) {\n      throw new ISE(\"No await set\");\n    }\n\n    return awaitChannels;\n  }\n\n\n  public List<ListenableFuture<?>> awaitableFutures()\n  {\n    if (!hasAwaitableFutures()) {\n      throw new ISE(\"No futures set\");\n    }\n\n    return awaitFutures;\n  }\n\n  /**\n   * Whether the processor has returned a value.\n   *\n   * This is the opposite of {@link #isAwait()}.\n   */\n  public boolean isReturn()\n  {\n    return awaitChannels == null && awaitFutures == null;\n  }\n\n  /**\n   * Whether the processor wants to be scheduled again.\n   *","sourceCodeStart":149,"sourceCodeEnd":185,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/frame/processor/ReturnOrAwait.java#L149-L185","documentation":"ReturnOrAwait.awaitableFutures() returns the list of ListenableFutures the processor is waiting on, valid only when the object is in the AWAIT_FUTURES state (hasAwaitableFutures()). Otherwise the union holds a return value or a channel set, and an ISE is thrown. The error is reported from run/combinedFuture call sites, meaning the scheduler tried to treat a non-futures outcome as a futures await.","triggerScenarios":"Calling awaitableFutures() on a ReturnOrAwait created via returnOrNull(...) or await(...) (channels), typically inside the processor run loop that combines futures with Futures.whenAllSucceed/combinedFuture.","commonSituations":"A processor returns a value early but the executor still probes for futures; a custom processor returns ReturnOrAwait.await(channels) while the runner was written for future-based processors.","solutions":["Check hasAwaitableFutures() before calling awaitableFutures() and handle the other states separately","Ensure the processor returns ReturnOrAwait.awaitFutures(...) when it intends to wait on futures","Audit the run loop so each ReturnOrAwait state (return / channels / futures) is dispatched to its own branch"],"exampleFix":"// before\nList<ListenableFuture<?>> futures = outcome.awaitableFutures();\n// after\nif (outcome.hasAwaitableFutures()) {\n  List<ListenableFuture<?>> futures = outcome.awaitableFutures();\n}","handlingStrategy":"type-guard","validationCode":"if (outcome.hasAwaitableFutures()) { futures = outcome.awaitableFutures(); }","typeGuard":"boolean isFutureAwait(ReturnOrAwait<?> o) { return o != null && o.hasAwaitableFutures(); }","tryCatchPattern":"try { futures = outcome.awaitableFutures(); } catch (IllegalStateException e) { if (!e.getMessage().contains(\"No futures set\")) throw e; /* handle return/channel states */ }","preventionTips":["Check hasAwaitableFutures() before reading futures in combinedFuture/run loops","Ensure future-based processors return ReturnOrAwait.awaitFutures(...)","Keep executor dispatch logic in sync with the ReturnOrAwait state model"],"tags":["java","frame-processor","state-error","listenable-future"],"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"}