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
- Order shutdown correctly: stop producers, await their completion, then shut down the executor
- Wrap submissions in try/catch for RejectedExecutionException and handle teardown gracefully
- Add an awaitTermination/graceful-stop step so in-flight submitters finish before shutdown
- 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
- Await producer completion (countdown latches/futures) before calling shutdown()
- Avoid double-shutdown paths that leave submitters racing the pool teardown
- In tests, shut down executors after awaiting async task completion
- Prefer unbounded or adequately sized queues when producers are bursty
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
- Executor already shutdown
- Got Interrupted while adding to the Queue
- Attempt to add row to swapped-out sink for segment
- Background lookup manager thread could not be cancelled
- can't start.
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)