eclipse-vertx/vert.x · error · IllegalArgumentException
Cannot schedule a timer with initialDelay < 0
Error message
Cannot schedule a timer with initialDelay < 0
What it means
Vert.x validates the initialDelay parameter of periodic timer scheduling and throws IllegalArgumentException when it is negative. A negative initial delay has no meaning in the underlying scheduler, so it is rejected before the timer is registered. Note delay itself must also be >= 1 ms (separate check).
Source
Thrown at vertx-core/src/main/java/io/vertx/core/impl/VertxImpl.java:693
options = new DnsClientOptions(options)
.setHost(address.getAddress().getHostAddress())
.setPort(address.getPort());
}
return new DnsClientImpl(this, options);
}
private long scheduleTimeout(ContextInternal context,
boolean periodic,
long initialDelay,
long delay,
TimeUnit timeUnit,
boolean addCloseHook,
Handler<Long> handler) {
if (delay < 1) {
throw new IllegalArgumentException("Cannot schedule a timer with delay < 1 ms");
}
if (initialDelay < 0) {
throw new IllegalArgumentException("Cannot schedule a timer with initialDelay < 0");
}
long timerId = timeoutCounter.getAndIncrement();
InternalTimerHandler task = new InternalTimerHandler(timerId, handler, periodic, context);
timeouts.put(timerId, task);
if (addCloseHook) {
context.addCloseHook(task);
}
EventLoop el = context.nettyEventLoop();
if (periodic) {
task.future = el.scheduleAtFixedRate(task, initialDelay, delay, timeUnit);
} else {
task.future = el.schedule(task, delay, timeUnit);
}
return task.id;
}
public long scheduleTimeout(ContextInternal context,
boolean periodic,View on GitHub (pinned to fb308bd8c3)
Solutions
- Clamp: initialDelay = Math.max(0, computedInitialDelay) before calling setPeriodic
- Fix the subtraction order so delay = deadline - now, and skip scheduling when the deadline already passed
- Validate the config value at load time and reject/normalize negatives early
Example fix
// before long initial = deadline - System.currentTimeMillis(); vertx.setPeriodic(initial, 1000, TimeUnit.MILLISECONDS, h -> tick()); // negative if deadline passed // after long initial = Math.max(0, deadline - System.currentTimeMillis()); vertx.setPeriodic(initial, 1000, TimeUnit.MILLISECONDS, h -> tick());
Defensive patterns
Strategy: validation
Validate before calling
if (initialDelayMs < 0) {
initialDelayMs = 0; // or skip scheduling
} Try / catch
try {
vertx.setPeriodic(initialDelayMs, periodMs, TimeUnit.MILLISECONDS, handler);
} catch (IllegalArgumentException e) {
// normalize initialDelay and retry once
} Prevention
- Clamp timestamp-derived delays with Math.max(0, deadline - now)
- Verify subtraction order when computing delays
- Validate negative timer config at load time
When it happens
Trigger: vertx.setPeriodic(initialDelay, delay, unit, handler) or the InternalTimerHandler scheduling path with initialDelay < 0, typically from a config-computed value like (deadline - now).
Common situations: Computing an initial delay from timestamps where the deadline already passed (now > deadline yields negative); misordered subtraction (deadline - now vs now - deadline); negative config values.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Cannot schedule a timer with delay < 1 ms
- Invalid reset code value
- Invalid window size: ${windowSize}
- Ping data must be exactly 8 bytes
- Invalid window size: ${windowSize}
AI-assisted analysis of eclipse-vertx/vert.x@fb308bd8c3 (2026-09-06).
Data as JSON: /api/errors/aa21826760fa0c39.
Report an issue: GitHub.