apache/druid · error · IllegalArgumentException
Cannot have a value when await != null or futures != null
Error message
Cannot have a value when await != null or futures != null
What it means
ReturnOrAwait is a sum type for FrameProcessor.run() results: either a return value OR a set of channels/futures to await, never both. Its constructor enforces this mutual exclusivity with IAE when retVal is non-null while awaitChannels or awaitFutures is also non-null. This keeps processor scheduling unambiguous.
Source
Thrown at processing/src/main/java/org/apache/druid/frame/processor/ReturnOrAwait.java:70
private final boolean awaitAllChannels;
@Nullable
private final List<ListenableFuture<?>> awaitFutures;
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).
*View on GitHub (pinned to 9b90983fd2)
Solutions
- Return exactly one mode: use ReturnOrAwait.value(...) for a result, or awaitChannels/awaitFutures factories for waiting, not both.
- In a custom processor, return the await object while work is pending and only produce a value when nothing is left to await.
- Review the processor's run() to ensure each return path sets either retVal or await fields, never both.
Example fix
// before return new ReturnOrAwait<>(result, channels, null, null); // IAE // after return channels.isEmpty() ? ReturnOrAwait.value(result) : ReturnOrAwait.awaitAll(channels);
Defensive patterns
Strategy: validation
Validate before calling
if (retVal != null && (awaitChannels != null || awaitFutures != null)) {
throw new IllegalArgumentException("ReturnOrAwait cannot carry both a value and await inputs");
} Type guard
boolean wellFormed(ReturnOrAwait<?> r) { return r != null; } // build only via value()/await*() factories Try / catch
try {
return ReturnOrAwait.value(result);
} catch (IllegalArgumentException e) {
// fall back to await path
} Prevention
- Only construct ReturnOrAwait via its static factories (value, awaitAll, awaitAny, awaitFutures).
- In run(), return await objects while pending work exists and values only when done.
- Keep each return path single-mode.
When it happens
Trigger: Building ReturnOrAwait via a factory or constructor with both a return value and await inputs, e.g. `ReturnOrAwait.value(x)` after also setting await fields, or custom code calling the (private/package) constructor with mixed parameters.
Common situations: Custom FrameProcessor implementations that accidentally return both a result and a pending await; misusing static factories like awaitAll/awaitAny together with a value.
Related errors
- Invalid stageNumber [%s]
- Only positive integers or zero can be added
- Cannot have null or empty column name
- Must have at least one input channel
- Partitions must all abut each other
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/2d75207d9e71c3cb.
Report an issue: GitHub.