Exception Handling

24.1 Wasm Exception Handling (exnref, Wasm 3.0)

Instructions: throw, throw_ref, try_table with catch, catch_ref, catch_all, catch_all_ref. The legacy try/catch/delegate/rethrow (phase-out) is still supported in browsers for older binaries.

JS interop: WebAssembly.Tag, WebAssembly.Exception (exception.is(tag), exception.getArg(tag, 0)). JS exceptions propagate through Wasm frames.

(module
  (tag $MyError (param i32))                       ;; exception tag with an i32 payload
  (export "MyError" (tag $MyError))

  (func $might_fail (param $x i32) (result i32)
    (if (i32.lt_s (local.get $x) (i32.const 0))
      (then (throw $MyError (local.get $x))))
    (i32.mul (local.get $x) (i32.const 2)))

  (func (export "safe") (param $x i32) (result i32)
    (block $handler (result i32)
      (try_table (result i32) (catch $MyError $handler)   ;; on MyError, branch to $handler with the payload
        (call $might_fail (local.get $x)))
      (return))
    ;; the payload (i32) is on the stack here
    (drop)
    (i32.const -1)))

24.2 Language Usage

C++: -fwasm-exceptions (zero-cost, native) vs the older Emscripten JS-based exceptions (-fexceptions, slower and larger).

Rust: panics are usually panic=abort in Wasm. Unwinding support with native exceptions is emerging (-Cpanic=unwind with nightly features).

Kotlin/Dart/OCaml WasmGC backends use native exceptions.