apache/druid · warning · RejectedExecutionException
Executor already shutdown
Error message
Executor already shutdown
What it means
DirectExecutorService.startTask() refuses new work once the executor has been shutdown, throwing RejectedExecutionException('Executor already shutdown'). DirectExecutorService runs tasks on the submitting thread under a lock, so after shutdown() any further execute() call is rejected. This mirrors Guava/ExecutorService rejection semantics.
Solutions
- Check executor.isShutdown() before submitting, or use an unshutdown owner for task submission
- Ensure shutdown() is called only after all producers have stopped submitting (join/await their completion first)
- Catch RejectedExecutionException around execute() and treat it as benign during teardown
- Restructure to use a lifecycle-managed executor (e.g. Druid's lifecycle scopes) so shutdown ordering is explicit
Example fix
// before
executor.execute(task);
// after
try {
executor.execute(task);
} catch (RejectedExecutionException e) {
// executor already shut down; drop or handle the task
} Defensive patterns
Strategy: try-catch
Validate before calling
// guard before submitting
if (!executor.isShutdown()) {
executor.execute(task);
} Try / catch
try {
executor.execute(task);
} catch (RejectedExecutionException e) {
// expected during shutdown; drop task or route to a dead-letter handler
} Prevention
- Establish strict shutdown ordering: producers stop before executors shut down
- Track executor lifecycle in one owner class; don't leak references post-shutdown
- Treat RejectedExecutionException as benign during service teardown
- Use Druid lifecycle-managed executors to centralize start/stop ordering
When it happens
Trigger: Calling execute(task) (via startTask) on a DirectExecutorService after shutdown() or shutdownNow() has been invoked on it.
Common situations: Service lifecycle races: a service's stop()/shutdown() runs while another thread (e.g. a query callback or background submitter) still submits tasks; reusing a cached executor reference after its owner shut it down.
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 is shutdown, rejecting task
- 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/1442864c7c2b7af5.
Report an issue: GitHub.
Appendix: source
Thrown at processing/src/main/java/org/apache/druid/java/util/common/concurrent/DirectExecutorService.java:151
} else {
long now = System.nanoTime();
TimeUnit.NANOSECONDS.timedWait(lock, nanos);
nanos -= System.nanoTime() - now; // subtract the actual time we waited
}
}
}
}
/**
* Checks if the executor has been shut down and increments the running task count.
*
* @throws RejectedExecutionException if the executor has been previously shutdown
*/
private void startTask()
{
synchronized (lock) {
if (shutdown) {
throw new RejectedExecutionException("Executor already shutdown");
}
runningTasks++;
}
}
/**
* Decrements the running task count.
*/
private void endTask()
{
synchronized (lock) {
int numRunning = --runningTasks;
if (numRunning == 0) {
lock.notifyAll();
}
}
}
}View on GitHub (pinned to 9b90983fd2)