apache/dubbo · error · RejectedExecutionException
The task queue does not have executor!
Error message
The task queue does not have executor!
What it means
Thrown by TaskQueue.offer() when the executor field is null. The TaskQueue is tightly coupled to EagerThreadPoolExecutor — its offer() logic (deciding whether to queue vs. create a new thread) needs to query the executor's pool size and active count. If setExecutor() was never called, the queue cannot function and rejects the offer. This indicates a misconfigured or incompletely constructed EagerThreadPool.
Source
Thrown at dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/eager/TaskQueue.java:47
*/
public class TaskQueue<R extends Runnable> extends LinkedBlockingQueue<Runnable> {
private static final long serialVersionUID = -2635853580887179627L;
private EagerThreadPoolExecutor executor;
public TaskQueue(int capacity) {
super(capacity);
}
public void setExecutor(EagerThreadPoolExecutor exec) {
executor = exec;
}
@Override
public boolean offer(Runnable runnable) {
if (executor == null) {
throw new RejectedExecutionException("The task queue does not have executor!");
}
int currentPoolThreadSize = executor.getPoolSize();
// have free worker. put task into queue to let the worker deal with task.
if (executor.getActiveCount() < currentPoolThreadSize) {
return super.offer(runnable);
}
// return false to let executor create new worker.
if (currentPoolThreadSize < executor.getMaximumPoolSize()) {
return false;
}
// currentPoolThreadSize >= max
return super.offer(runnable);
}
/**View on GitHub (pinned to 3a3043227f)
Solutions
- Ensure setExecutor(eagerThreadPoolExecutor) is called on the TaskQueue before any task is submitted — the standard Dubbo EagerThreadPool factory does this automatically.
- Do not use TaskQueue with a plain ThreadPoolExecutor; it is designed exclusively for EagerThreadPoolExecutor.
- If constructing manually: TaskQueue<Runnable> queue = new TaskQueue<>(capacity); EagerThreadPoolExecutor exec = new EagerThreadPoolExecutor(..., queue, ...); queue.setExecutor(exec);
- Use Dubbo's built-in thread pool SPI (threadpool=eager) instead of manual construction.
Example fix
// before — TaskQueue without executor wiring TaskQueue<Runnable> queue = new TaskQueue<>(256); ThreadPoolExecutor pool = new ThreadPoolExecutor(4, 16, 60, SECONDS, queue, factory); pool.execute(task); // throws: executor is null // after — wire executor before use TaskQueue<Runnable> queue = new TaskQueue<>(256); EagerThreadPoolExecutor pool = new EagerThreadPoolExecutor(4, 16, 60, SECONDS, queue, factory, abortHandler); queue.setExecutor(pool); pool.execute(task);
Defensive patterns
Strategy: validation
Validate before calling
// Ensure the executor is wired before use
public static TaskQueue<Runnable> createWiredQueue(int capacity, EagerThreadPoolExecutor exec) {
TaskQueue<Runnable> queue = new TaskQueue<>(capacity);
queue.setExecutor(exec);
return queue;
}
// Verify before first execute:
if (queueHasExecutor(queue)) { // reflectively or via a wrapper that tracks state
pool.execute(task);
} Try / catch
try {
pool.execute(task);
} catch (RejectedExecutionException e) {
if (e.getMessage().contains("does not have executor")) {
throw new IllegalStateException("TaskQueue not wired to its executor — call setExecutor()", e);
}
throw e;
} Prevention
- Always call queue.setExecutor(executor) immediately after constructing both objects.
- Prefer Dubbo's built-in threadpool=eager SPI which handles wiring automatically.
- Never pass a TaskQueue to a plain ThreadPoolExecutor — it is EagerThreadPoolExecutor-specific.
- Write a factory method that constructs and wires both objects atomically.
When it happens
Trigger: A TaskQueue is constructed and used by a ThreadPoolExecutor (directly or via framework wiring) without calling setExecutor(executor) first. This happens if the TaskQueue is passed to a plain ThreadPoolExecutor instead of EagerThreadPoolExecutor, or if framework initialization code that wires the executor was bypassed.
Common situations: Custom thread pool construction that uses TaskQueue without calling setExecutor(); a bug in framework/SPI initialization; manually constructing an EagerThreadPoolExecutor where the setExecutor() call was omitted; upgrading Dubbo versions where the TaskQueue wiring contract changed.
Related errors
- Queue capacity is full.
- already exists bean with same name and type, name=${name}, t
- expected single matching bean but found ${size} candidates f
- Method [{}] unimplemented.
- Cache store path can't be created: {}
AI-assisted analysis of apache/dubbo@3a3043227f (2026-08-14).
Data as JSON: /api/errors/ab70354550f90725.
Report an issue: GitHub.