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
- Await in two steps: first ReturnOrAwait.awaitAll/awaitAny on channels, then a separate ReturnOrAwait.awaitFutures(...) once channels are ready.
- Convert one wait kind into the other (e.g. wrap futures as channels or vice versa) so a single await collection is used.
- 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
- Never mix channel and future awaits in one step; chain them across run() iterations.
- Track a single pending-await collection per processor.
- Prefer factory methods over direct construction.
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
- No such stage[%s]
- Writable channel is not available. The output channel might
- Frame allocator is not available. The output channel might b
- Cannot have a value when await != null or futures != null
- Expected single element
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/2395b4a70b805b3c.
Report an issue: GitHub.