workerd: setTimeout clamped to 30 seconds, timeout-based retry broken
Fixes retry and backoff logic broken by workerd clamping setTimeout to 30 seconds. This skill shows how to cap in-request delays and move longer waits to Queues with delaySeconds, Durable Object alarms, or cron triggers. Use it when a Worker schedules retries or delays longer than 30 seconds and they fire early or never.
Fix setTimeout clamped to 30 seconds, timeout-based retry broken
TL;DR
Do not use setTimeout for waits longer than 30 seconds in Workers - the runtime clamps the timer and your retry fires early. Keep in-request delays under 30 seconds, and move longer waits to a Queue with delaySeconds, a Durable Object alarm, or a cron trigger.
Verbatim error
workerd: setTimeout clamped to 30 seconds, timeout-based retry brokenSteps
- Confirm the clamp: log the requested delay and the actual elapsed time around the timer. Expected: delays over 30 seconds fire at roughly 30 seconds.
- For backoff that fits inside a request, cap each delay at 25 seconds and keep the total retry budget small. Expected: retries fire on the schedule you set.
- For longer waits, switch to a Queue: send the retry as a message with
delaySecondsset. Expected: the message redelivers after the requested delay. - Alternative for one-shot future wakeups: use a Durable Object
alarm(). Expected: the alarm fires at the target time. - Alternative for periodic retries: use a cron trigger that picks up pending work. Expected: the trigger runs on schedule.
- Verify by tailing logs around the expected fire time. Expected: the retry runs at the intended delay, not at 30 seconds.
Use this when
setTimeout(60000)fires at 30 seconds in a Worker- Exponential backoff stalls or collapses to a fixed 30-second rhythm
- Logs mention timer clamping in workerd
Not for this skill when
- All your timeouts are under 30 seconds (they work fine)
- You need a request-scoped deadline inside the CPU limit (different pattern)
- The problem is
setIntervaldrift rather than the clamp (same fix family, but check the interval logic)
Variant phrasings
- workerd timer max 30 seconds
- setTimeout not working long delay cloudflare workers
- retry backoff broken workers
- timeout clamped workers runtime
Why it happens
Workers are event-driven isolates without long-lived processes, so the runtime caps timers at 30 seconds to bound isolate lifetime. The design intent is short-lived execution: anything that needs to happen later should be scheduled through queues, crons, or alarms rather than held in a timer.
Edge cases
setIntervalhas the same 30-second clamp.- Timers keep running during streaming responses but are still capped at 30 seconds.
- There is no
unref()in workerd; do not port Node timer patterns blindly.
Provenance
Resolved from the public thread: https://vectle.com/posts/pst_5oU6qXmn4NzeEJ5-xhTwvQ
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.