apache/druid · warning · RejectedExecutionException

Executor is shutdown, rejecting task

Error message

Executor is shutdown, rejecting task

What it means

Execs' ThreadPoolExecutor uses a custom RejectedExecutionHandler that, when the executor is already shutdown, throws RejectedExecutionException('Executor is shutdown, rejecting task') instead of applying a fallback policy. Normally the handler would put the task on the queue; shutdown makes that pointless and unsafe, so it fails fast.

Solutions

  1. Order shutdown correctly: stop producers, await their completion, then shut down the executor
  2. Wrap submissions in try/catch for RejectedExecutionException and handle teardown gracefully
  3. Add an awaitTermination/graceful-stop step so in-flight submitters finish before shutdown
  4. Use a lifecycle framework hook so executor shutdown always happens after dependent services stop

Example fix

// before
service.stop();
worker.submit(work); // throws if worker's executor already stopped
// after
worker.stop();      // stop submitters first
service.stop();
Defensive patterns

Strategy: try-catch

Validate before calling

if (executor.isShutdown()) {
  throw new IllegalStateException("Executor already stopped; not submitting");
}

Try / catch

try {
  executor.submit(task);
} catch (RejectedExecutionException e) {
  log.debug("Task rejected during shutdown: %s", e.getMessage());
}

Prevention

When it happens

Trigger: A task is submitted (execute/submit) to a ThreadPoolExecutor created via Execs after shutdown()/shutdownNow(); the saturation handler detects executor.isShutdown() and throws rather than queuing.

Common situations: Shutdown races in ingestion/query services where workers keep submitting while the pool is being torn down; double-shutdown of a service followed by late submissions; tests that shut down executors in @After while async work still posts tasks.

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/233b57e550c189b2. Report an issue: GitHub.

Appendix: source

Thrown at processing/src/main/java/org/apache/druid/java/util/common/concurrent/Execs.java:163

    if (capacity > 0) {
      queue = new ArrayBlockingQueue<>(capacity);
    } else {
      queue = new SynchronousQueue<>();
    }
    return new ThreadPoolExecutor(
        nThreads,
        nThreads,
        0L,
        TimeUnit.MILLISECONDS,
        queue,
        makeThreadFactory(nameFormat, priority),
        new RejectedExecutionHandler()
        {
          @Override
          public void rejectedExecution(Runnable r, ThreadPoolExecutor executor)
          {
            if (executor.isShutdown()) {
              throw new RejectedExecutionException("Executor is shutdown, rejecting task");
            }
            try {
              executor.getQueue().put(r);
            }
            catch (InterruptedException e) {
              throw new RejectedExecutionException("Got Interrupted while adding to the Queue", e);
            }
          }
        }
    );
  }

  public static ListeningExecutorService directExecutor()
  {
    return new DirectExecutorService();
  }
}

View on GitHub (pinned to 9b90983fd2)