apache/druid · error · IllegalStateException

No futures set

Error message

No futures set

What it means

ReturnOrAwait.awaitableFutures() returns the list of ListenableFutures the processor is waiting on, valid only when the object is in the AWAIT_FUTURES state (hasAwaitableFutures()). Otherwise the union holds a return value or a channel set, and an ISE is thrown. The error is reported from run/combinedFuture call sites, meaning the scheduler tried to treat a non-futures outcome as a futures await.

Solutions

  1. Check hasAwaitableFutures() before calling awaitableFutures() and handle the other states separately
  2. Ensure the processor returns ReturnOrAwait.awaitFutures(...) when it intends to wait on futures
  3. Audit the run loop so each ReturnOrAwait state (return / channels / futures) is dispatched to its own branch

Example fix

// before
List<ListenableFuture<?>> futures = outcome.awaitableFutures();
// after
if (outcome.hasAwaitableFutures()) {
  List<ListenableFuture<?>> futures = outcome.awaitableFutures();
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (outcome.hasAwaitableFutures()) { futures = outcome.awaitableFutures(); }

Type guard

boolean isFutureAwait(ReturnOrAwait<?> o) { return o != null && o.hasAwaitableFutures(); }

Try / catch

try { futures = outcome.awaitableFutures(); } catch (IllegalStateException e) { if (!e.getMessage().contains("No futures set")) throw e; /* handle return/channel states */ }

Prevention

When it happens

Trigger: Calling awaitableFutures() on a ReturnOrAwait created via returnOrNull(...) or await(...) (channels), typically inside the processor run loop that combines futures with Futures.whenAllSucceed/combinedFuture.

Common situations: A processor returns a value early but the executor still probes for futures; a custom processor returns ReturnOrAwait.await(channels) while the runner was written for future-based processors.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

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

  /**
   * The set of channels the processors wants to wait for. Valid if {@link #isAwait()} is true.
   *
   * Numbers in this set correspond to positions in the {@link FrameProcessor#inputChannels()} list.
   */
  public IntSet awaitableChannels()
  {
    if (!hasAwaitableChannels()) {
      throw new ISE("No await set");
    }

    return awaitChannels;
  }


  public List<ListenableFuture<?>> awaitableFutures()
  {
    if (!hasAwaitableFutures()) {
      throw new ISE("No futures set");
    }

    return awaitFutures;
  }

  /**
   * Whether the processor has returned a value.
   *
   * This is the opposite of {@link #isAwait()}.
   */
  public boolean isReturn()
  {
    return awaitChannels == null && awaitFutures == null;
  }

  /**
   * Whether the processor wants to be scheduled again.
   *

View on GitHub (pinned to 9b90983fd2)