{"record":{"id":"c14db19415861dc5","repo":"apache/druid","slug":"no-channels-set","errorCode":null,"errorMessage":"No channels set","messagePattern":"No channels set","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/frame/processor/ReturnOrAwait.java","lineNumber":217,"sourceCode":"    return awaitFutures != null;\n  }\n\n  /**\n   * Whether the processor wants to wait for a set of channels. If true, {@link #awaitableChannels()} contains the\n   * set of channels to wait for, and {@link #isAwaitAllChannels()}.\n   */\n  public boolean hasAwaitableChannels()\n  {\n    return awaitChannels != null;\n  }\n\n  /**\n   * Whether the processor wants to wait for all channels in {@link #awaitableChannels()} (true), or any channel (false)\n   */\n  public boolean isAwaitAllChannels()\n  {\n    if (!hasAwaitableChannels()) {\n      throw new ISE(\"No channels set\");\n    }\n\n    return awaitAllChannels;\n  }\n\n  @Override\n  public String toString()\n  {\n    if (hasAwaitableChannels()) {\n      return \"await channels=\" + (awaitAllChannels ? \"all\" : \"any\") + awaitChannels;\n    } else if (hasAwaitableFutures()) {\n      return \"await futures=\" + awaitFutures;\n    } else {\n      return \"return=\" + retVal;\n    }\n  }\n\n  private static IntSet rangeSet(final int count)","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/frame/processor/ReturnOrAwait.java#L199-L235","documentation":"isAwaitAllChannels() reports whether the processor wants ALL channels in awaitableChannels() satisfied (vs any), but it is only defined when the object is in the AWAIT_CHANNELS state. If there is no channel set (RETURN or AWAIT_FUTURES state), an ISE with 'No channels set' is thrown. Like the other ReturnOrAwait accessors, it guards variant misuse of this union type.","triggerScenarios":"The run loop calls isAwaitAllChannels() on an outcome that has no awaitable channels — e.g. after isAwait() returns true it skips the hasAwaitableChannels() check, or the outcome came from awaitFutures(...)/returnOrNull(...).","commonSituations":"Scheduler code that assumes every await outcome is a channel-await and reads awaitAllChannels for the waitAny/waitAll decision; processors that switched from channel-based to future-based awaiting but the runner wasn't updated.","solutions":["Verify hasAwaitableChannels() before calling isAwaitAllChannels()","Dispatch on the ReturnOrAwait state first (isReturn, hasAwaitableChannels, hasAwaitableFutures) and only read awaitAllChannels in the channels branch","Make the processor return ReturnOrAwait.await(channels, awaitAllChannels) so the flag is populated when channels are awaited"],"exampleFix":"// before\nboolean all = outcome.isAwaitAllChannels();\n// after\nif (outcome.hasAwaitableChannels()) {\n  boolean all = outcome.isAwaitAllChannels();\n}","handlingStrategy":"type-guard","validationCode":"if (outcome.hasAwaitableChannels()) { allChannels = outcome.isAwaitAllChannels(); }","typeGuard":"boolean hasChannelConfig(ReturnOrAwait<?> o) { return o != null && o.hasAwaitableChannels(); }","tryCatchPattern":"try { all = outcome.isAwaitAllChannels(); } catch (IllegalStateException e) { if (!e.getMessage().contains(\"No channels set\")) throw e; /* not a channel await */ }","preventionTips":["Read awaitAllChannels only inside the hasAwaitableChannels() branch","Structure run loops as an if/else over isReturn/hasAwaitableChannels/hasAwaitableFutures","Test processors that use both await-all and await-any semantics"],"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"}