apache/dubbo · warning · RejectedExecutionException
Number of pending timeouts ({}) is greater than or equal to
Error message
Number of pending timeouts ({}) is greater than or equal to maximum allowed pending timeouts ({}) What it means
RejectedExecutionException thrown by HashedWheelTimer.newTimeout() when the number of pending (not-yet-fired) timeouts exceeds the configured maxPendingTimeouts limit. The pending timeout counter is incremented atomically before the check; if it exceeds the cap, it is decremented back and the exception is thrown. This is a backpressure mechanism to prevent unbounded memory growth from excessive scheduled timeouts. Only enforced when maxPendingTimeouts > 0 (0 or negative means unlimited).
Source
Thrown at dubbo-common/src/main/java/org/apache/dubbo/common/timer/HashedWheelTimer.java:396
@Override
public boolean isStop() {
return WORKER_STATE_SHUTDOWN == WORKER_STATE_UPDATER.get(this);
}
@Override
public Timeout newTimeout(TimerTask task, long delay, TimeUnit unit) {
if (task == null) {
throw new NullPointerException("task");
}
if (unit == null) {
throw new NullPointerException("unit");
}
long pendingTimeoutsCount = pendingTimeouts.incrementAndGet();
if (maxPendingTimeouts > 0 && pendingTimeoutsCount > maxPendingTimeouts) {
pendingTimeouts.decrementAndGet();
throw new RejectedExecutionException("Number of pending timeouts ("
+ pendingTimeoutsCount + ") is greater than or equal to maximum allowed pending "
+ "timeouts (" + maxPendingTimeouts + ")");
}
start();
// Add the timeout to the timeout queue which will be processed on the next tick.
// During processing all the queued HashedWheelTimeouts will be added to the correct HashedWheelBucket.
long deadline = System.nanoTime() + unit.toNanos(delay) - startTime;
// Guard against overflow.
if (delay > 0 && deadline < 0) {
deadline = Long.MAX_VALUE;
}
HashedWheelTimeout timeout = new HashedWheelTimeout(this, task, deadline);
timeouts.add(timeout);
return timeout;
}View on GitHub (pinned to 3a3043227f)
Solutions
- Increase maxPendingTimeouts if the workload legitimately requires many concurrent pending timeouts.
- Reduce the rate of newTimeout calls or ensure timeouts fire promptly — investigate whether the worker thread is blocked.
- Cancel timeouts that are no longer needed (Timeout.cancel()) to free pending slots.
- If maxPendingTimeouts is unset (0/negative), the limit is disabled — set it to a value matching your expected concurrency as a safety net.
Example fix
// before — unlimited pending timeouts causes memory risk, or limit too low new HashedWheelTimer(factory, 100, TimeUnit.MILLISECONDS, 512, 100); // scheduling 101+ tasks throws // after — set a higher limit matching expected load new HashedWheelTimer(factory, 100, TimeUnit.MILLISECONDS, 512, 100000); // or disable limit entirely (0 or negative) new HashedWheelTimer(factory, 100, TimeUnit.MILLISECONDS, 512, 0);
Defensive patterns
Strategy: try-catch
Validate before calling
// Check pending count before scheduling (requires access to pendingTimeouts())
if (timer.pendingTimeouts() < maxPending) {
timer.newTimeout(task, delay, unit);
} else {
applyBackpressure(); // throttle, queue, or reject at application level
} Try / catch
try {
timer.newTimeout(task, delay, unit);
} catch (RejectedExecutionException e) {
if (e.getMessage().contains("pending timeouts")) {
logger.warn("Timer pending timeout limit reached, backing off", e);
backoffAndRetry(task, delay, unit); // exponential backoff
} else {
throw e;
}
} Prevention
- Set maxPendingTimeouts to a value matching expected concurrency, or 0 to disable the limit.
- Cancel timeouts (Timeout.cancel()) when they are no longer needed to free pending slots.
- Monitor timer.pendingTimeouts() and alert if it trends upward (indicates tasks not firing).
- Ensure the worker thread is not blocked — a stalled worker causes pending timeouts to accumulate.
When it happens
Trigger: Scheduling timeouts faster than they fire, accumulating pending timeouts beyond the maxPendingTimeouts limit configured at construction. This indicates the timer is being overwhelmed — tasks are scheduled but not completing (either very long delays or the worker thread is stalled).
Common situations: High-throughput scheduling of short-lived timeouts where the fire rate can't keep up; long delay values causing accumulation; worker thread blocked or slow; misconfigured maxPendingTimeouts that is too low for the workload; a leak where timeouts are scheduled but never cancelled/fired.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- ticksPerWheel may not be greater than 2^30: {}
- no more memory can be used !
- Thread pool is EXHAUSTED! Thread Name: %s, Pool Size: %d (ac
- Queue capacity is full.
- threadFactory
AI-assisted analysis of apache/dubbo@3a3043227f (2026-08-14).
Data as JSON: /api/errors/6298ca43caff56e7.
Report an issue: GitHub.