walkor/workerman · error · RuntimeException
Event::addTimer($interval) failed
Error message
Event::addTimer($interval) failed
What it means
The repeat() half of the same libevent adapter: it arms a PERSIST \Event timer with addTimer($interval) and throws when libevent refuses the value. The overwhelming cause is a negative or otherwise invalid interval passed through Workerman\Timer::add($interval, $func, $args, true) (the default persistent mode). Because repeat() timers drive heartbeats and periodic tasks, an exception here aborts the callback registration at runtime.
Source
Thrown at src/Events/Event.php:147
* {@inheritdoc}
*/
public function offRepeat(int $timerId): bool
{
return $this->offDelay($timerId);
}
/**
* {@inheritdoc}
*/
public function repeat(float $interval, callable $func, array $args = []): int
{
$className = $this->eventClassName;
$timerId = $this->timerId++;
$event = new $className($this->eventBase, -1, $className::TIMEOUT | $className::PERSIST, function () use ($func, $args) {
$this->safeCall($func, $args);
});
if (!$event->addTimer($interval)) {
throw new \RuntimeException("Event::addTimer($interval) failed");
}
$this->eventTimer[$timerId] = $event;
return $timerId;
}
/**
* {@inheritdoc}
*/
public function onReadable($stream, callable $func): void
{
$className = $this->eventClassName;
$fdKey = (int)$stream;
$event = new $className($this->eventBase, $stream, $className::READ | $className::PERSIST, $func);
if ($event->add()) {
$this->readEvents[$fdKey] = $event;
}
}
View on GitHub (pinned to 1391112a61)
Solutions
- Validate/clamp before registering: `$interval = max(0.001, (float)$interval); Timer::add($interval, $fn);`.
- Reject non-positive config values at load time instead of letting them reach the timer (fail fast with a clear config error).
- If positive intervals also fail, check ext-event health (php --ri event) or switch loop implementations via Worker::$eventLoopClass.
Example fix
// before: interval from config can be 0 or negative
Timer::add((float)$config['heartbeat_interval'], [$this, 'ping']);
// RuntimeException: Event::addTimer(0) failed / Event::addTimer(-1) failed
// after: validate at load time, clamp at use time
$interval = (float)($config['heartbeat_interval'] ?? 0);
if ($interval <= 0) {
throw new InvalidArgumentException('heartbeat_interval must be > 0');
}
Timer::add($interval, [$this, 'ping']); Defensive patterns
Strategy: validation
Validate before calling
function addIntervalTimer(float $interval, callable $fn): int
{
if ($interval <= 0) {
throw new InvalidArgumentException('interval must be > 0, got ' . var_export($interval, true));
}
return Workerman\Timer::add($interval, $fn); // persistent=true -> repeat()
} Type guard
function isSchedulableInterval(mixed $interval): bool
{
return is_numeric($interval) && (float)$interval > 0;
} Try / catch
try {
Timer::add($interval, $fn);
} catch (RuntimeException $e) {
Worker::log('timer registration failed: ' . $e->getMessage());
throw $e; // do not silently drop periodic tasks (heartbeats, flushers)
} Prevention
- Treat heartbeat/interval config as validated input: positive float, cast once at load.
- Default missing interval config to a sane constant instead of passing 0/null through.
- Smoke-test the production event loop (ext-event) in CI so addTimer failures surface before deploy.
When it happens
Trigger: Timer::add() with persistent=true (default) and a non-positive interval — e.g. interval read from config as 0/-1, an interval computed as a difference that went negative, or interval passed as a string like '-5'. Only with the ext-event Event loop selected.
Common situations: Heartbeat/keepalive tasks where the interval comes from per-connection settings and can be unset or zero; cron-like next-run calculations; unit-tested code running under a loop that behaves differently than the production libevent loop.
Related errors
- Event::addTimer($delay) failed
- $timeInterval can not less than 0
- Timer can only be used in workerman running environment
- %s::$eventLoopClass must implement %s
- Bad remoteAddress
AI-assisted analysis of walkor/workerman@1391112a61 (2026-08-21).
Data as JSON: /api/errors/d3edbaf76e295824.
Report an issue: GitHub.