Making Rust Workers reliable: panic and abort recovery in wasm‑bindgen (opens in new tab)
Rust Workers historically treated Rust panics and aborts as fatal WebAssembly failures, potentially poisoning a Worker instance and causing unrelated requests to fail. Cloudflare’s latest work upstreamed into wasm-bindgen adds comprehensive recovery: panic=unwind preserves application state after recoverable panics, while abort handling ensures Rust code cannot run again after an unrecoverable abort.
Initial Recovery Mitigations
- Early Rust Workers used a custom panic handler to track failures and reinitialize the entire application before serving later requests.
- JavaScript bindings were wrapped with Proxy-based indirection so every Rust entry point passed through recovery logic.
- Generated bindings were modified to reinitialize the WebAssembly module after failures.
- This approach shipped by default in
workers-rs0.6 and prevented persistent failure modes, but reinitialization could discard in-memory state.
Panic Unwinding with WebAssembly Exception Handling
- WebAssembly’s
wasm32-unknown-unknowntarget traditionally defaults topanic=abort, turning panics into traps andWebAssembly.RuntimeErrorexceptions. - With WebAssembly Exception Handling support, Rust can be compiled using:
RUSTFLAGS='-Cpanic=unwind' cargo build -Zbuild-std - Unwinding allows Rust destructors to run, preserving state and cleaning up resources instead of terminating the entire instance.
std::panic::catch_unwindcan translate a Rust panic into a recoverableResult.
Changes to wasm-bindgen
- The Walrus WebAssembly parser was updated to understand
try/catchexception-handling instructions. - The descriptor interpreter was updated to evaluate code containing exception blocks.
- Generated exports now catch Rust panics at the Rust–JavaScript boundary and expose them as
PanicErrorexceptions. - Async exports reject their JavaScript promises with
PanicError. - Exported functions use
extern "C-unwind"so unwinding is explicitly permitted across the boundary. - A
MaybeUnwindSafetrait checksUnwindSaferequirements only when compiling withpanic=unwind. - For closures that cannot safely unwind,
Closure::new_abortingprovides an explicit alternative that terminates on panic rather than risking invalid state.
Results of panic=unwind
- Panics in exported Rust functions are caught by
wasm-bindgen. - JavaScript receives a
PanicError. - Async calls reject their promises instead of poisoning the Worker.
- Rust destructors execute correctly.
- The WebAssembly instance remains valid and reusable.
- Stateful applications, including Durable Objects, can recover without losing all in-memory state.
Abort Recovery
panic=unwindcannot handle aborts such as out-of-memory failures because aborts do not unwind.- The remaining recovery mechanism prevents Rust code from being re-entered after an abort, avoiding repeated execution in a corrupted WebAssembly state.
- Together, unwinding and abort recovery prevent one failed request from poisoning sibling or future requests.
The recommended approach is to use the latest wasm-bindgen and Rust Workers releases, enabling panic=unwind where state preservation matters while using explicit aborting closures when unwind safety cannot be guaranteed.