Stabilizing Rust Workers with panic and abort recovery in wasm-bindgen
Key point
panic=unwind and abort recovery prevent sandbox poisoning in Rust Workers.
Details
Rust Workers compile Rust into WebAssembly to run on Cloudflare Workers, but a panic or unexpected abort could corrupt the runtime state, affecting even other requests. The core issue was that wasm-bindgen, which generates the Rust-to-JavaScript bindings, had no recovery semantics.
The initial response was a custom Rust panic handler that tracked failure state inside the Worker, along with reinitializing the entire application after a failure. On the JavaScript side, a Proxy-based wrapper around the Rust-JavaScript boundary handled all entrypoints consistently, and the generated bindings were also modified to be reinitialized. This approach became the default starting with workers-rs 0.6.
The subsequent goal was to recover from panics without discarding state. To achieve this, panic=unwind was made to work on top of WebAssembly Exception Handling, and the standard library itself was rebuilt with unwind support using RUSTFLAGS='-Cpanic=unwind' cargo build -Zbuild-std. Even when an exception occurs, destructors now run normally, and std::panic::catch_unwind also works correctly on Wasm.
This process required several fixes to the wasm-bindgen toolchain as well.
- Added
try/catchinstruction parsing support to Walrus - Improved the descriptor interpreter that interprets exception handling blocks
- Catching panics at the Rust-JavaScript boundary and exposing them as JavaScript
PanicError - Async exports reject their Promise with a
PanicError - Marking exports with
extern "C-unwind"to allow unwinding across the boundary
Closures required separate handling. A new trait called MaybeUnwindSafe checks for UnwindSafe only when panic=unwind is in effect, and a Closure::new_aborting variant was added for cases where unwind-safety cannot be guaranteed, causing it to abort instead of unwind on panic.
With this in place, even when a panic occurs, the following is guaranteed:
- Panics in exported Rust functions are caught by wasm-bindgen
- They are delivered to JavaScript as PanicError
- Async exports have their Promise rejected
- Rust destructors run normally
- The WebAssembly instance remains reusable
Aborts still remain an issue. In cases like OOM, unwinding is impossible, so state recovery itself is not possible, but it was still necessary to detect and isolate aborts so that subsequent requests don't inherit a corrupted state. To do this, Exception.Tag was used to distinguish between abort and unwind, and a new abort hook, set_on_abort, along with a reentrancy guard, were added to prevent corrupted state from spreading to the next call.
This work was also extended to library use cases of wasm-bindgen. The experimental --reset-state-function makes it possible to reset internal state back to its initial state without re-importing or recreating the Wasm instance; class handles from the old instance become orphaned, but new classes can still be created. In other words, even if the app encounters an error, it doesn't end up completely bricked.
In the long term, work was also done in parallel to make Rust's Wasm support more stable overall. Modern WebAssembly Exception Handling is supported in v8 13.8.1 (2025-04-28), workerd v1.20250620.0 (2025-06-19), Chrome 138 (2025-06-28), Firefox 131 (2024-10-01), Safari 18.4 (2025-03-31), and Node.js 25.0.0 (2025-10-15). There was particular concern about being stuck with legacy EH due to Node.js 24 LTS, but Cloudflare backported the feature to support it on both the Node.js 24 and 22 lines.
The latest Rust Workers gained a --panic-unwind flag starting with 0.8.0. With this flag, panics are recovered more safely, and aborts are also handled via new classification/recovery hooks so they don't affect the next request.
This summary was generated automatically by AI. Check the original for the author's claims and context. Copyright belongs to the original author.
Our guide explains how the AI works. Report summary errors, attribution issues, or removal requests via Contact.