apache/druid · error · IllegalStateException

Cannot have both awaitChannels and awaitFutures

Error message

Cannot have both awaitChannels and awaitFutures

What it means

ReturnOrAwait supports awaiting either channels or futures but not both simultaneously; the constructor throws ISE when awaitChannels and awaitFutures are both non-null. The scheduler would otherwise not know which wait semantics to apply.

Source

Thrown at processing/src/main/java/org/apache/druid/frame/processor/ReturnOrAwait.java:74

  private ReturnOrAwait(
      @Nullable T retVal,
      @Nullable IntSet awaitChannels,
      @Nullable List<ListenableFuture<?>> awaitFutures,
      final boolean awaitAllChannels
  )
  {
    this.retVal = retVal;
    this.awaitChannels = awaitChannels;
    this.awaitAllChannels = awaitAllChannels;
    this.awaitFutures = awaitFutures;

    if (retVal != null && (awaitChannels != null || awaitFutures != null)) {
      throw new IAE("Cannot have a value when await != null or futures != null");
    }

    if (awaitChannels != null && awaitFutures != null) {
      throw new ISE("Cannot have both awaitChannels and awaitFutures");
    }
  }

  /**
   * Wait for nothing; that is: run again as soon as possible.
   */
  public static <T> ReturnOrAwait<T> runAgain()
  {
    return new ReturnOrAwait<>(null, IntSets.emptySet(), null, true);
  }

  /**
   * Wait for all provided channels to become readable (or finished).
   *
   * Numbers in this set correspond to positions in the {@link FrameProcessor#inputChannels()} list.
   *
   * It is OK to pass in a mutable set, because this method does not modify the set or retain a reference to it.
   */

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Await in two steps: first ReturnOrAwait.awaitAll/awaitAny on channels, then a separate ReturnOrAwait.awaitFutures(...) once channels are ready.
  2. Convert one wait kind into the other (e.g. wrap futures as channels or vice versa) so a single await collection is used.
  3. Fix the custom processor's run() to track a single pending-await collection at a time.

Example fix

// before
return new ReturnOrAwait<>(null, channels, null, futures); // ISE
// after
if (!channels.isEmpty()) {
  return ReturnOrAwait.awaitAll(channels);
}
return ReturnOrAwait.awaitFutures(futures);
Defensive patterns

Strategy: validation

Validate before calling

if (awaitChannels != null && awaitFutures != null) {
  throw new IllegalArgumentException("Await either channels or futures, not both");
}

Type guard

boolean singleAwaitMode(Collection<?> channels, Collection<?> futures) { return channels == null || futures == null; }

Try / catch

try {
  return ReturnOrAwait.awaitFutures(futures);
} catch (IllegalStateException e) {
  // split into sequential awaits
}

Prevention

When it happens

Trigger: Constructing a ReturnOrAwait with both awaitChannels (a Collection of ReadableFrameChannel) and awaitFutures (a Collection of Future) set, typically from a custom processor run() implementation that mixes the two wait styles.

Common situations: Custom FrameProcessor code that accumulates channels to await and also defers to async futures in the same step; misusing static factory methods by passing both collections.

Understand the failure class

Background: "mutually exclusive" flag errors: what "can't supply both nx and xx", "--raw is not compatible with -i" and "cannot be used with" mean, and how to fix them — this error's family across 29 libraries.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/2395b4a70b805b3c. Report an issue: GitHub.