{"record":{"id":"e45d9d78858365b3","repo":"apache/druid","slug":"no-await-set","errorCode":null,"errorMessage":"No await set","messagePattern":"No await set","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/frame/processor/ReturnOrAwait.java","lineNumber":157,"sourceCode":"  @Nullable\n  public T value()\n  {\n    if (isAwait()) {\n      throw new ISE(\"No value yet\");\n    }\n\n    return retVal;\n  }\n\n  /**\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   *","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/frame/processor/ReturnOrAwait.java#L139-L175","documentation":"ReturnOrAwait.awaitableChannels() returns the set of input-channel positions the processor wants to wait on, but only when the object is in the AWAIT_CHANNELS state (hasAwaitableChannels()). In any other state (RETURN or AWAIT_FUTURES) there is no channel set, so an ISE is thrown. It is an internal invariant check guarding against reading the wrong variant of this union type.","triggerScenarios":"Calling awaitableChannels() on a ReturnOrAwait produced by ReturnOrAwait.returnOrNull(...) or ReturnOrAwait.awaitFutures(...) — i.e. the runner inspects channels without first confirming the await-channels variant.","commonSituations":"FrameProcessorExecutor/run loops modified to always query channels; mixing up ReturnOrAwait factory methods so an await-futures result is dispatched through the channel-waiting path.","solutions":["Check hasAwaitableChannels() before calling awaitableChannels()","Branch on isReturn()/hasAwaitableFutures() first and dispatch each state to its proper handler","Construct the ReturnOrAwait with ReturnOrAwait.await(...) (the channels variant) when the processor intends to wait on input channels"],"exampleFix":"// before\nIntSet channels = outcome.awaitableChannels();\n// after\nif (outcome.hasAwaitableChannels()) {\n  IntSet channels = outcome.awaitableChannels();\n}","handlingStrategy":"type-guard","validationCode":"if (outcome.hasAwaitableChannels()) { channels = outcome.awaitableChannels(); }","typeGuard":"boolean isChannelAwait(ReturnOrAwait<?> o) { return o != null && o.hasAwaitableChannels(); }","tryCatchPattern":"try { channels = outcome.awaitableChannels(); } catch (IllegalStateException e) { if (!\"No await set\".equals(e.getMessage())) throw e; /* fall through to return/futures handling */ }","preventionTips":["Dispatch on all ReturnOrAwait states before accessing variant-specific data","Use the ReturnOrAwait factory methods that match the intended wait mode","Cover each state branch in unit tests of custom processors"],"tags":["java","frame-processor","state-error"],"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"}