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

  1. Ensure no new tasks are submitted after executor.shutdown() — coordinate shutdown with an 'accepting' flag checked before submit.
  2. Use executor.awaitTermination() in your shutdown sequence and stop submitting before calling shutdown().
  3. Catch RejectedExecutionException at the submission site and treat it as a shutdown signal (log and discard or queue for retry on restart).
  4. 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

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


AI-assisted analysis of apache/dubbo@3a3043227f (2026-08-14). Data as JSON: /api/errors/1739254003d5c090. Report an issue: GitHub.