{"record":{"id":"b590a47a3bb68b5c","repo":"walkor/workerman","slug":"event-addtimer-delay-failed","errorCode":null,"errorMessage":"Event::addTimer($delay) failed","messagePattern":"Event::addTimer\\(\\$delay\\) failed","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"src/Events/Event.php","lineNumber":109,"sourceCode":"        } else {\n            $className = '\\EventBase';\n        }\n        $this->eventBase = new $className();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function delay(float $delay, callable $func, array $args = []): int\n    {\n        $className = $this->eventClassName;\n        $timerId = $this->timerId++;\n        $event = new $className($this->eventBase, -1, $className::TIMEOUT, function () use ($func, $args, $timerId) {\n            unset($this->eventTimer[$timerId]);\n            $this->safeCall($func, $args);\n        });\n        if (!$event->addTimer($delay)) {\n            throw new \\RuntimeException(\"Event::addTimer($delay) failed\");\n        }\n        $this->eventTimer[$timerId] = $event;\n        return $timerId;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function offDelay(int $timerId): bool\n    {\n        if (isset($this->eventTimer[$timerId])) {\n            $this->eventTimer[$timerId]->del();\n            unset($this->eventTimer[$timerId]);\n            return true;\n        }\n        return false;\n    }\n","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/walkor/workerman/blob/1391112a61d23020e11e7b89f17050f6cfaea431/src/Events/Event.php#L91-L127","documentation":"This is Workerman's libevent (ext-event) event-loop adapter. delay() schedules one-shot timers by constructing an \\Event with TIMEOUT and calling addTimer($delay); libevent returns false when the timer cannot be armed — in practice when the delay is negative (or the event base is broken). The adapter turns that boolean into a RuntimeException so failures are not silently dropped callbacks.","triggerScenarios":"Workerman\\Timer::add($t, $fn, $args, false) (persistent=false maps to delay()) with $t <= 0 — typically a negative interval computed from a deadline ('now - startTime' after the deadline passed), or the event extension being misconfigured such that every addTimer fails. Fires only when the loop in use is the Event (libevent) implementation.","commonSituations":"Retry/backoff code computing 'next attempt time - now' that goes negative under load; passing -1 as a 'no delay' sentinel; crontab-style schedules that compute a negative offset; environments where ext-event is installed (so it is auto-selected) but its event base is unavailable.","solutions":["Clamp the interval before scheduling: `$delay = max(0.001, $delay); Timer::add($delay, $fn, [], false);`.","Find where the negative value originates (deadline math like $deadline - microtime(true)) and guard it at the source.","If every timer fails even with positive delays, verify the ext-event installation (php --ri event) or force a different loop, e.g. `Worker::$eventLoopClass = Workerman\\Events\\Ev::class;` (or revolt/Fiber)."],"exampleFix":"// before: $delay goes negative once the deadline has passed\nTimer::add($deadline - microtime(true), $fn, [], false);\n// RuntimeException: Event::addTimer(-2.5) failed\n\n// after: clamp to a minimal positive interval\nTimer::add(max(0.001, $deadline - microtime(true)), $fn, [], false);","handlingStrategy":"validation","validationCode":"function scheduleOnce(float $delay, callable $fn): int\n{\n    if ($delay <= 0) {\n        $delay = 0.001; // or log and skip\n    }\n    return Workerman\\Timer::add($delay, $fn, [], false);\n}","typeGuard":"function isSchedulableDelay(mixed $delay): bool\n{\n    return is_numeric($delay) && (float)$delay > 0;\n}","tryCatchPattern":"try {\n    Timer::add($delay, $fn, [], false);\n} catch (RuntimeException $e) {\n    // libevent refused the timer; degrade to immediate execution\n    $fn();\n}","preventionTips":["Never derive one-shot delays from unbounded differences; clamp with max(0.001, $delay).","Validate numeric, positive intervals from config before they reach Timer::add.","Pin a known-good event loop in tests to catch loop-specific timer behavior before production."],"tags":["workerman","timer","libevent","event-loop","php"],"backgroundTag":"invalid-timer-interval","analyzedSha":"1391112a61d23020e11e7b89f17050f6cfaea431","analyzedAt":"2026-08-21T02:05:46.744Z","schemaVersion":2},"datasetVersion":"2026-08-21T03:17:12.404Z"}