apache/dubbo · error · IllegalArgumentException

tickDuration: %d (expected: 0 < tickDuration in nanos < %d

Error message

tickDuration: %d (expected: 0 < tickDuration in nanos < %d

What it means

IllegalArgumentException thrown by the HashedWheelTimer constructor as an overflow guard. After converting tickDuration to nanoseconds (unit.toNanos), it checks whether the nanosecond value is so large that multiplying by wheel.length would overflow a long. The message reports the offending tickDuration and the maximum allowed value (Long.MAX_VALUE / wheel.length). This prevents arithmetic overflow in the tick-scheduling math (tickDuration * tick in waitForNextTick).

Source

Thrown at dubbo-common/src/main/java/org/apache/dubbo/common/timer/HashedWheelTimer.java:253

            throw new NullPointerException("unit");
        }
        if (tickDuration <= 0) {
            throw new IllegalArgumentException("tickDuration must be greater than 0: " + tickDuration);
        }
        if (ticksPerWheel <= 0) {
            throw new IllegalArgumentException("ticksPerWheel must be greater than 0: " + ticksPerWheel);
        }

        // Normalize ticksPerWheel to power of two and initialize the wheel.
        wheel = createWheel(ticksPerWheel);
        mask = wheel.length - 1;

        // Convert tickDuration to nanos.
        this.tickDuration = unit.toNanos(tickDuration);

        // Prevent overflow.
        if (this.tickDuration >= Long.MAX_VALUE / wheel.length) {
            throw new IllegalArgumentException(String.format(
                "tickDuration: %d (expected: 0 < tickDuration in nanos < %d",
                tickDuration, Long.MAX_VALUE / wheel.length));
        }
        workerThread = threadFactory.newThread(worker);

        this.maxPendingTimeouts = maxPendingTimeouts;

        if (INSTANCE_COUNTER.incrementAndGet() > INSTANCE_COUNT_LIMIT &&
            WARNED_TOO_MANY_INSTANCES.compareAndSet(false, true)) {
            reportTooManyInstances();
        }
    }

    @Override
    protected void finalize() throws Throwable {
        try {
            super.finalize();
        } finally {

View on GitHub (pinned to 3a3043227f)

Solutions

  1. Use a reasonable tickDuration — 100ms is standard for I/O timeouts. If you need very long timeouts, use a larger wheel size or schedule tasks with the intended absolute deadline, not the tick duration.
  2. Ensure the unit matches the magnitude: if the value is in milliseconds, pass TimeUnit.MILLISECONDS, not TimeUnit.SECONDS.
  3. Cap the tickDuration to a sane maximum before construction.

Example fix

// before — huge tick duration in hours
new HashedWheelTimer(factory, Long.MAX_VALUE, TimeUnit.HOURS, 512);

// after — reasonable tick with long timeouts handled by wheel rounds
new HashedWheelTimer(factory, 100, TimeUnit.MILLISECONDS, 512);
Defensive patterns

Strategy: validation

Validate before calling

long tickNanos = unit.toNanos(tickDuration);
long maxNanos = Long.MAX_VALUE / 512; // or the actual wheel size
if (tickNanos >= maxNanos) {
    throw new IllegalArgumentException("tickDuration too large for wheel size");
}
new HashedWheelTimer(factory, tickDuration, unit, ticksPerWheel);

Prevention

When it happens

Trigger: Passing an extremely large tickDuration relative to the wheel size — e.g., tickDuration = Long.MAX_VALUE with a unit of SECONDS, or a very large value in a coarse unit (DAYS/HOURS) that converts to a nanosecond value exceeding Long.MAX_VALUE / wheel.length. The combination of large duration and wheel size makes the internal tick deadline computation overflow.

Common situations: Configuration that specifies a timeout interval in DAYS or HOURS that is unreasonably large; unit mismatch causing a value intended as milliseconds to be interpreted as a larger unit; computed durations from arithmetic that accidentally produces huge values.

Related errors


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