apache/dubbo · warning · RejectedExecutionException
Executor is shutdown!
Error message
Executor is shutdown!
What it means
Thrown by TaskQueue.retryOffer() when the associated executor's isShutdown() returns true. retryOffer() is called by EagerThreadPoolExecutor.execute() as a fallback after the initial submit is rejected — if the pool has been shut down in the meantime (or was already shutting down), retrying the offer is pointless and this RejectedExecutionException is thrown instead of silently dropping the task.
Source
Thrown at dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/eager/TaskQueue.java:74
// return false to let executor create new worker.
if (currentPoolThreadSize < executor.getMaximumPoolSize()) {
return false;
}
// currentPoolThreadSize >= max
return super.offer(runnable);
}
/**
* retry offer task
*
* @param o task
* @return offer success or not
* @throws RejectedExecutionException if executor is terminated.
*/
public boolean retryOffer(Runnable o, long timeout, TimeUnit unit) throws InterruptedException {
if (executor.isShutdown()) {
throw new RejectedExecutionException("Executor is shutdown!");
}
return super.offer(o, timeout, unit);
}
}
View on GitHub (pinned to 3a3043227f)
Solutions
- Ensure no new tasks are submitted after executor.shutdown() — coordinate shutdown with an 'accepting' flag checked before submit.
- Use executor.awaitTermination() in your shutdown sequence and stop submitting before calling shutdown().
- Catch RejectedExecutionException at the submission site and treat it as a shutdown signal (log and discard or queue for retry on restart).
- Fix resource lifecycle ordering so the Dubbo/client subsystem is fully drained before the executor is shut down.
Example fix
// before — submitting during shutdown
volatile boolean accepting = true;
// ... shutdown path calls executor.shutdown() ...
executor.execute(task); // throws if shutdown in progress
// after — check accepting flag before submit
if (!accepting || executor.isShutdown()) {
handleGracefulRejection(task);
return;
}
executor.execute(task); Defensive patterns
Strategy: validation
Validate before calling
// Check shutdown state before submitting
if (!executor.isShutdown() && !executor.isTerminated()) {
executor.execute(task);
} else {
logger.debug("Executor is shutdown, discarding task");
handleShutdownDiscard(task);
} Try / catch
try {
executor.execute(task);
} catch (RejectedExecutionException e) {
if (executor.isShutdown()) {
logger.debug("Task rejected because executor is shutting down", e);
handleShutdownDiscard(task); // log and drop, or persist for later
} else {
throw e; // genuine capacity issue, not shutdown
}
} Prevention
- Coordinate shutdown: stop accepting new work before calling executor.shutdown().
- Maintain a volatile 'accepting' flag checked before every submit.
- Drain pending work (awaitTermination) before the container/framework fully tears down.
- Catch RejectedExecutionException at submit sites and treat shutdown-triggered rejections as non-error.
When it happens
Trigger: Submitting a task to an EagerThreadPoolExecutor that is in the process of shutting down (shutdown() has been called). The initial execute() is rejected (e.g., pool saturated during shutdown), the retry path checks isShutdown(), finds it true, and throws. Also occurs if tasks are submitted from a shutdown hook or during application teardown.
Common situations: Application shutdown race — tasks submitted while the Dubbo framework or a custom executor is shutting down; shutdown hooks triggering invocations; reactive/async pipelines that outlive the executor's lifecycle; resource cleanup ordering issues.
Related errors
- ScopeBeanFactory is destroyed
- ExtensionDirector is destroyed
- ExtensionLoader is destroyed: ${type}
- Thread pool is EXHAUSTED! Thread Name: %s, Pool Size: %d (ac
- Queue capacity is full.
AI-assisted analysis of apache/dubbo@3a3043227f (2026-08-14).
Data as JSON: /api/errors/1739254003d5c090.
Report an issue: GitHub.