Cloudflare taught wasm-bindgen to catch a Rust panic. Workers no longer poison the sandbox.
Three Cloudflare engineers shipped panic and abort recovery into wasm-bindgen on April 22. A Rust Worker that panics now reinitialises on the next request.
Cloudflare’s WebAssembly team shipped panic and abort recovery upstream into wasm-bindgen on April 22, the standard glue layer between Rust compiled to Wasm and the JavaScript host that runs it. A Rust Worker that panics in production no longer breaks every request that follows. The Wasm instance reinitialises, and the next request runs against fresh state.
This is one of those changes that sounds incremental until you’re the person on call. Until now, a single unwrap() on None could leave a Rust Worker stuck returning 500s until the next deploy. The work was done by Guy Bedford, Hood Chatham, and Logan Gatlin, and it sits in the same direction as Cloudflare’s broader push to make Workers the default runtime for serverless Rust.
What used to happen on a Rust panic
When Rust code compiled to Wasm panics, the panic propagates up through the Wasm call stack. In the default panic=abort configuration most Rust-on-Wasm projects use, the panic immediately aborts the entire Wasm instance. The host JavaScript catches a generic RuntimeError, the request returns 500, and the instance state is now corrupt: any statics, any in-progress allocator state, any threadlocal hooks are in an undefined state.
The old fix was to throw the instance away and re-instantiate. That’s expensive in cold-start terms, so runtimes that prioritise low latency tended to instead just keep using the poisoned instance. Cloudflare Workers fell into that bucket. The result was the well-known behaviour Rust-Worker users learned to work around: one panic and every subsequent request from the same isolate would either crash in the same place or return a useless error. The fix was usually “deploy a no-op change to force a new isolate.”
What the change actually does
The new design has three layers, each landing in a different part of the toolchain.
WebAssembly Exception Handling in the parser. Cloudflare added try/catch instruction support to Walrus, the Wasm parser wasm-bindgen uses. Without parser support, wasm-bindgen couldn’t even read modules that compiled with panic=unwind. This is the unglamorous prerequisite; the work made it possible to ship modules with proper exception-style control flow.
A new descriptor interpreter that evaluates exception blocks. wasm-bindgen generates JavaScript glue by interpreting “descriptors” embedded in the Wasm module. Those descriptors needed to know how to walk past try/catch blocks without choking. The interpreter learned to.
PanicError exceptions at the Rust-JavaScript boundary. The final piece is the wasm-bindgen-generated export wrappers. They now catch Rust panics at the FFI boundary and surface them as JavaScript PanicError exceptions, instead of letting them abort the instance. A new Exception.Tag field marks each exception as recoverable or not, so the host can distinguish “developer-fixable bug” from “out-of-memory, restart everything.”
For abort cases that genuinely can’t unwind (a stack overflow, an out-of-memory, an unreachable), Cloudflare added a set_on_abort handler that calls a schedule_reinit() hook. The next request transparently reinitialises the Wasm instance before running. The aborting request still fails. Every subsequent one doesn’t.
What you actually have to do
The good news for most readers is “nothing.” The changelog entry for workers-rs 0.6.5 says the basic panic-recovery path is enabled by default with no configuration. Existing Rust Workers picked it up on their next deploy after the September workers-rs bump.
If you want the full abort-recovery story too, you build with RUSTFLAGS='-Cpanic=unwind' cargo build -Zbuild-std and use workers-rs 0.8.0, which exposes a --panic-unwind flag. The build is bigger because it pulls in unwinding tables, and you have to opt into MaybeUnwindSafe for any closure that crosses the FFI boundary. For most apps the default is enough.
Why it matters beyond Cloudflare
The interesting part of this story is that none of it is Cloudflare-only. The changes are upstream in wasm-bindgen, which means every Rust-on-Wasm project using wasm-bindgen, in browsers, in Node, in Deno, in any of the new dedicated Wasm runtimes, can adopt the same model. Cloudflare also backported modern WebAssembly Exception Handling to Node.js 22 and 24 LTS, so the same panic-recovery path now works in plain Node servers.
That’s a shift in posture. wasm-bindgen has historically been the Rust-on-browser tool that everyone uses but no one wants to maintain. Cloudflare has effectively volunteered to be its long-term steward, which mirrors what they’ve done with service-worker-mock and miniflare over the last five years. Rust is gaining ground in the JavaScript ecosystem on the back of the Bun migration, the Bun mainline merge earlier this month, and the Rspack/Rolldown build-tool churn. A serious story for Rust running in production Wasm is exactly the missing link that turns “Rust compiles to Wasm” into “Rust is a viable backend language for the JS ecosystem.”
The Exception Handling instructions Cloudflare lit up are themselves recent. The WebAssembly working group finalised the proposal as a stage-4 feature in late 2024, and Node 22 was the first runtime to ship support behind a flag. Cloudflare’s backport into the 22 LTS line and the cleaner support in 24 LTS is what makes Exception-Handling-based panic recovery a portable production technique rather than a Worker-only trick. The same path opens for AssemblyScript, MoonBit, and any other source language that targets Wasm: once the host knows how to unwind through try/catch, every guest language can adopt the same lifecycle contract.
A second consequence is testing. Code that previously had to be defensive about “what happens if my upstream panics inside Wasm” can now write tests that assert the recovery path runs. The MaybeUnwindSafe trait gives the type system a way to mark closures that aren’t safe to unwind through, instead of relying on convention. That changes the review burden for anyone integrating third-party crates into a Worker; you can now point to a compile-time signal instead of doing the review in your head.
What’s still missing
Cloudflare’s April 22 post is honest about three rough edges the team did not fully solve in workers-rs 0.8.0.
- Static state across recovery. A panic clears the Wasm instance and therefore any module-level
staticdata. Durable-Object-style external state survives, but per-isolate caches do not. The blog post recommends keeping anything that has to survive a panic in Durable Object storage or a KV namespace. - Cold-start cost on abort recovery. Reinitialising a Wasm instance is fast but not free. On a Worker serving 10,000 RPS, a stream of aborts would still degrade tail latency until the cause is fixed. Cloudflare did not publish a benchmark for the recovery path.
MaybeUnwindSafeergonomics. The new trait is opt-in for FFI closures. Adopting it across a large existing codebase is a non-trivial refactor; the team called this out as “future work.”
What this means for you
If you write Rust for Cloudflare Workers, the practical answer is to bump workers-rs to 0.6.5 or later for free panic recovery, and to opt into 0.8.0 plus the panic=unwind build flag if you ship Workers that absolutely cannot fall over on the unhappy path. If you ship Rust to plain Node.js, upgrade to a Node 22 or 24 LTS release that includes the backported Exception Handling support. If you maintain a Wasm-targeted Rust crate, this is the day you finally get to stop documenting “this library will panic if X” as a hard contract; the host can now catch and recover. And if you’re a JavaScript developer reading this and wondering whether Rust-on-Wasm is finally usable in production, the honest answer is “it has been for a year, and the on-call experience just got materially better.” The slop of Rust-Worker bug reports that ended in “redeploy to force a new isolate” should disappear from issue trackers over the next quarter.
Share this article
Quick reference
Sources
- Making Rust Workers reliable: panic and abort recovery in wasm-bindgen — Cloudflare
- Panic Recovery for Rust Workers (changelog) — Cloudflare Workers Developer Docs
- workers-rs on GitHub — GitHub
Frequently Asked
- Do I have to change my Worker code to get panic recovery?
- No. Panic recovery is automatic for all workers-rs deployments on version 0.6.5 or later. Abort recovery requires building with the panic=unwind compiler flag and using workers-rs 0.8.0 or later.
- What happens to the in-flight request that triggered the panic?
- It returns a 500 error. Recovery applies to the next request, not the one that crashed. State held in module-level statics is reset on recovery; Durable Object storage survives because it's external to the Wasm instance.
- Is this only useful on Cloudflare Workers?
- No. The changes shipped upstream to wasm-bindgen and to Node.js 22 and 24 LTS releases. Any Rust-on-Wasm app whose host can run modern Exception Handling instructions can use the same panic-recovery model.