The error, verbatim
Jump to the fix ↓X [ERROR] Your Worker failed validation because it exceeded startup limits.
A request to the Cloudflare API (/accounts/.../workers/scripts/nk-repro-cpu/versions) failed.
- Error: Script startup exceeded CPU time limit.
[code: 10021]
Tested on
- wrangler
- 4.139.0
- Worker
- compatibility_date 2026-09-01
- Plan
- Workers Paid
- OS
- Windows 11 Pro
Contents
Everything at the top level of your Worker, outside the fetch handler, runs once when Cloudflare starts it up, before any request. Cloudflare checks how long that takes when you deploy, and rejects the upload if it’s too slow. Nothing about your requests is involved yet.
The fix
1. Find the slow code. When the deploy fails, wrangler writes a CPU profile of the startup for you and prints where:
A CPU Profile of your Worker's startup phase has been written to .wrangler\tmp\startup-profile-qtzDoy\worker.cpuprofile - load it into the Chrome DevTools profiler (or directly in VSCode) to view a flamegraph.You can also make one before deploying:
npx wrangler check startup2. Do that work ahead of time, not at startup. If the slow part builds a value that’s the same every time (a lookup table, a parsed file, a computed constant), compute it once in a build script and ship the result in the bundle.
What I measured
A Worker doing a big calculation at the top level, deployed to a test Worker with wrangler 4.139.0:
| Top-level work | Deploy |
|---|---|
| lighter version | accepted, Worker Startup Time: 435 ms |
| heavier version | rejected, Script startup exceeded CPU time limit [code: 10021] |
| result shipped as a constant | accepted, Worker Startup Time: 2 ms |
Wrangler’s own advice under the error:
Try reducing the amount of work done during startup (outside the event handler), either by removing code or relocating it inside the event handler.
Why moving it into the handler isn’t enough
“Relocate it inside the event handler” gets the deploy through, but the work still happens, just on a request instead. I moved the same calculation into fetch, computed once and then kept in a variable:
So only move it into the handler if it’s work that doesn’t have to happen on most requests. If every request needs it, precompute it.
How this was tested
A test Worker on workers.dev, deployed with wrangler 4.139.0 on a Workers Paid account: a top-level loop sized so one version passed and one failed the startup check, the same loop moved lazily into fetch, and the result shipped as a constant. Startup times and errors are copied from wrangler’s output, and the per-request CPU from wrangler tail. Cloudflare’s limits page says startup must finish within 1 second; the exact point where my test crossed it wasn’t measured.
— N.K., end of entry No.035