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

  1. Increase maxPendingTimeouts if the workload legitimately requires many concurrent pending timeouts.
  2. Reduce the rate of newTimeout calls or ensure timeouts fire promptly — investigate whether the worker thread is blocked.
  3. Cancel timeouts that are no longer needed (Timeout.cancel()) to free pending slots.
  4. 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

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

Related errors


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