Rust Employees run on the Cloudflare Employees platform by compiling Rust to WebAssembly, however as we’ve discovered, WebAssembly has some sharp edges. When issues go unsuitable with a panic or an sudden abort, the runtime could be left in an undefined state. For customers of Rust Employees, panics had been traditionally deadly, poisoning the occasion and presumably even bricking the Employee for a time frame.
Whereas we had been capable of detect and mitigate these points, there remained a small probability {that a} Rust Employee would unexpectedly fail and trigger different requests to fail together with it. An unhandled Rust abort in a Employee affecting one request may escalate right into a broader failure affecting sibling requests and even proceed to have an effect on new incoming requests. The foundation explanation for this was in wasm-bindgen, the core undertaking that generates the Rust-to-JavaScript bindings Rust Employees depend upon, and its lack of built-in restoration semantics.
On this put up, we’ll share how the most recent model of Rust Employees handles complete Wasm error restoration that solves this abort-induced sandbox poisoning. This work has been contributed again into wasm-bindgen as a part of our collaboration throughout the wasm-bindgen group fashioned final yr. First with panic=unwind assist, which ensures {that a} single failed request by no means poisons different requests, after which with abort restoration mechanisms that assure Rust code on Wasm can by no means re-execute after an abort.
Preliminary restoration mitigations
Our preliminary makes an attempt to deal with reliability on this space centered on understanding and containing failures attributable to Rust panics and aborts in manufacturing Rust Employees. We launched a customized Rust panic handler that tracked failure state inside a Employee and triggered full utility reinitialization earlier than dealing with subsequent requests. On the JavaScript aspect, this required wrapping the Rust-JavaScript name boundary utilizing Proxy‑based mostly indirection to make sure that all entrypoints had been constantly encapsulated. We additionally made focused modifications to the generated bindings to appropriately reinitialize the WebAssembly module after a failure.
Whereas this method relied on customized JavaScript logic, it demonstrated that dependable restoration was achievable and eradicated the persistent failure modes we had been seeing in apply. This resolution was shipped by default to all staff‑rs customers beginning in model 0.6, and it laid the groundwork for the extra normal, upstreamed abort restoration mechanisms described within the sections that observe.
Implementing panic=unwind with WebAssembly Exception Dealing with
The abort restoration mechanisms described above be certain that a Employee can survive a failure, however they accomplish that by reinitializing your complete utility. For stateless request handlers, that is high quality. However for workloads that maintain significant state in reminiscence, equivalent to Sturdy Objects, reinitialization means shedding that state completely. A single panic in a single request may wipe the in-memory state being utilized by different concurrent requests.
In most native Rust environments, panics could be unwound, permitting destructors to run and this system to get better with out shedding state. In WebAssembly, issues traditionally regarded very totally different. Rust compiled to Wasm by way of wasm32-unknown-unknown defaults to panic=abort, so a panic inside a Rust Employee would abruptly entice with an unreachable instruction and exit Wasm again to JS with a WebAssembly.RuntimeError.
To get better from panics with out discarding occasion state, we wanted panic=unwind assist for wasm32-unknown-unknown in wasm-bindgen, made doable by the WebAssembly Exception Dealing with proposal, which gained vast engine assist in 2023.
We begin by compiling with RUSTFLAGS='-Cpanic=unwind' cargo construct -Zbuild-std, which rebuilds the usual library with unwind assist and generates code with correct panic unwinding. For instance:
struct HasDropA;
struct HasDropB;
extern "C" {
fn imported_func();
}
fn some_func() {
let a = HasDropA;
let b = HasDropB;
imported_func();
}compiles to WebAssembly as:
attempt
name
catch_all
name
name
rethrow
finish
name
name This ensures that even when imported_func() panics, destructors nonetheless run. Equally, std::panic::catch_unwind(|| some_func()) compiles into:
attempt
name
;; set consequence to Okay(return worth)
catch
attempt
name <:panicking::catch_unwind::cleanup>
;; set consequence to Err(panic payload)
catch_all
name <:panicking::cannot_unwind>
unreachable
finish
finish Getting this to work end-to-end required a number of adjustments to the wasm-bindgen toolchain. The WebAssembly parser Walrus didn’t know the best way to deal with attempt/catch directions, so we added assist for them. The descriptor interpreter additionally wanted to be taught the best way to consider code containing exception dealing with blocks. At that time, the complete utility could possibly be constructed with panic=unwind.
The ultimate step was modifying the exports generated by wasm-bindgen to catch panics on the Rust-JavaScript boundary and floor them as JavaScript PanicError exceptions. One subtlety: Rust will catch international exceptions and abort when unwinding by means of extern "C" capabilities, so exports wanted to be marked extern "C-unwind" to explicitly permit unwinding throughout the boundary. For futures, a panic rejects the JavaScript Promise with a PanicError.
Closures required particular consideration to make sure unwind security was correctly checked, by way of a brand new MaybeUnwindSafe trait that checks UnwindSafe solely when constructed with panic=unwind. This rapidly uncovered an issue, although: many closures seize references that stay after an unwind, making them inherently unwind-unsafe. To keep away from a state of affairs the place customers are inspired to incorrectly wrap closures in AssertUnwindSafe simply to fulfill the compiler, we added Closure::new_aborting variants, which terminate on panic as an alternative of unwinding in circumstances the place unwind security cannot be assured.
With panic unwinding enabled:
Panics in exported Rust capabilities are caught by wasm-bindgen
Panics floor to JavaScript as PanicError exceptions
Async exports reject their returned guarantees with a PanicError
Rust destructors run appropriately
The WebAssembly occasion stays legitimate and reusable
The complete particulars of the method and the best way to use it in wasm-bindgen are lined within the newest information web page for Wasm Bindgen: Catching Panics.
Even with panic=unwind assist, aborts nonetheless occur – out-of-memory errors being one frequent trigger. As a result of aborts can’t unwind, there isn’t any risk of state restoration in any respect, however we are able to not less than detect and get better from aborts for future operations to keep away from invalid state erroring subsequent requests.
Panic unwind assist launched a brand new downside for abort restoration. Once we obtain an error from Wasm we don’t know if it got here from an extern “C-unwind” international error, or if it was a real abort. Aborts can take many shapes in WebAssembly.
We had two choices to unravel this technically: both mark all errors that are undoubtedly aborts, or mark all errors that are undoubtedly unwinds. Both may have labored however we selected the latter. Since our international exception dealing with was immediately utilizing uncooked WAT-level (WebAssembly textual content format) Exception Dealing with directions already, we discovered it simpler to implement exception tags for international exceptions to differentiate them from aborting non-unwind-safe exceptions.
With the power to obviously distinguish between recoverable and non-recoverable errors because of this Exception.Tag characteristic in WebAssembly Exception Dealing with, we had been capable of then combine each a brand new abort handler in addition to abort reentrancy guards.
A brand new abort hook, set_on_abort, can be utilized at initialization time to connect a handler that recovers accordingly for the platform embedding’s wants.
Hardening panic and abort dealing with is crucial to avoiding invalid execution state. WebAssembly permits deeply interleaved name stacks, the place Wasm can name into JavaScript and JavaScript can re-enter Wasm at arbitrary depths, whereas alongside this, a number of duties could be functioning in the identical occasion. Beforehand, an abort occurring in a single process or nested stack was not assured to invalidate increased stacks by means of JS, resulting in undefined conduct. Care was required to make sure we are able to assure the execution mannequin, and contribution on this area stays ongoing.
Whereas aborts are by no means very best, and reinitialization on failure is an absolute worst-case state of affairs, implementing crucial error restoration because the final line of protection ensures execution correctness and that future operations will be capable of succeed. The invalid state doesn’t persist, guaranteeing a single failure doesn’t cascade into a number of failures.
Extension: abort reinitialization for wasm-bindgen libraries
Whereas we had been engaged on this, we realized that it is a frequent downside for libraries utilized by JS which are constructed with wasm-bindgen, and that they might additionally profit from attaching an abort handler to have the ability to carry out restoration.
However when constructing Wasm as an ES module and importing it immediately (e.g. by way of import { func } from ‘wasm-dep’), it’s not clear what the restoration mechanism can be for a Wasm abort whereas calling func() for an already-linked and initialized library that’s in a person JS utility.
Whereas not strictly a Rust Employees use case, our workforce additionally helps JS-based Employees customers who run Rust-backed Wasm library dependencies. If we may repair this downside on the similar time, that might not directly additionally profit Wasm utilization on the Cloudflare Employees platform.
To assist automated abort restoration for Wasm library use circumstances, we added assist for an experimental reinitialization mechanism into wasm‑bindgen, --reset-state-function. This exposes a perform that permits the Rust utility to successfully request that it reset its inner Wasm occasion again to its preliminary state for the following name, with out requiring shoppers of the generated bindings to reimport or recreate them. Class situations from the previous occasion will throw as their handles turn out to be orphaned, however new lessons can then be constructed. The JS utility utilizing a Wasm library is errored however not bricked.
The complete technical particulars of this characteristic and the best way to use it in wasm-bindgen are lined within the new wasm-bindgen information part Wasm Bindgen: Dealing with Aborts.
Maturing the Rust Wasm Exception Dealing with ecosystem
Upstream contributions for this work didn’t cease on the wasm-bindgen undertaking. Constructing for Wasm with panic=unwind nonetheless requires an experimental nightly Rust goal, so we’ve additionally been working to advance Rust’s Wasm assist for WebAssembly Exception Dealing with to assist carry this to steady Rust.
In the course of the improvement of WebAssembly Exception Dealing with, a late‑stage specification change resulted in two variants: legacy exception dealing with and the ultimate trendy exception dealing with “with exnref”. At present, Rust’s WebAssembly targets nonetheless default to emitting code for the legacy variant. Whereas legacy exception dealing with is broadly supported, it’s now deprecated.
Fashionable WebAssembly Exception Dealing with is supported as of the next JS platform releases:
Runtime | Model | Launch Date |
v8 | 13.8.1 | April 28, 2025 |
workerd | v1.20250620.0 | June 19, 2025 |
Chrome | 138 | June 28, 2025 |
Firefox | 131 | October 1, 2024 |
Safari | 18.4 | March 31, 2025 |
Node.js | 25.0.0 | October 15, 2025 |
As we had been investigating the assist matrix, the biggest concern ended up being the Node.js 24 LTS launch schedule, which might have left your complete ecosystem caught on legacy WebAssembly Exception Dealing with till April 2028.
Having found this discrepancy, we had been capable of backport trendy exception dealing with to the Node.js 24 launch, and even backport the fixes wanted to make it work on the Node.js 22 launch line to make sure assist for this goal. This could permit the trendy Exception Dealing with proposal to turn out to be the default goal subsequent yr.
Over the approaching months, we’ll be working to make the transition to steady panic=unwind and trendy Exception Dealing with as invisible as doable to finish customers.
Whereas these lengthy‑time period investments within the ecosystem take time, they assist construct a stronger basis for the Rust WebAssembly group as a complete, and we’re glad to have the ability to contribute to those enhancements.
Utilizing panic unwind in Rust Employees
As of model 0.8.0 of Rust Employees, we have now a brand new --panic-unwind flag, which could be added to the construct command, following the directions right here.
With this flag, panics could be absolutely recovered, and abort restoration will use the brand new abort classification and restoration hook mechanism. We extremely advocate upgrading and making an attempt it out for a extra steady Rust Employees expertise, and plan to make panic=unwind the default in a subsequent launch. Customers remaining on panic=abort will nonetheless proceed to reap the benefits of the earlier customized restoration wrapper dealing with from 0.6.0.
Committing to Rust Employees stability
This work is a part of our ongoing effort in the direction of a steady launch for Rust Employees. By fixing these sharp edges of the Wasm platform foundations at their root, and contributing again to the ecosystem the place it is sensible, we construct stronger foundations not only for our platform, however your complete Rust, JS, and Wasm ecosystem.
We’ve got quite a lot of future enhancements deliberate for Rust Employees, and we’ll quickly be sharing updates on this extra work, together with wasm-bindgen generics and automatic bindgen, which Man Bedford from our workforce previewed in a chat on Rust & JS Interoperability at Wasm.io final month.
Discover us in #rust‑on‑staff on the Cloudflare Discord. We additionally welcome suggestions and dialogue and particularly all new contributors to the workers-rs and wasm-bindgen GitHub tasks.



