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

  1. Return exactly one mode: use ReturnOrAwait.value(...) for a result, or awaitChannels/awaitFutures factories for waiting, not both.
  2. In a custom processor, return the await object while work is pending and only produce a value when nothing is left to await.
  3. 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

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


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