Structured WASM panic stack traces for browsers.
wacks captures Error.stack from inside a WASM panic hook, parses it into structured Frames across Chrome, Firefox, and Safari, and demangles Rust symbols — giving you data suitable for error reporting services (PostHog, Sentry, Datadog, etc.).
Function names are always resolved from the WASM binary's name section, making symbolication reliable across all browsers — including Safari/WebKit, which nondeterministically drops names from Error.stack.
wacks::Builder::new()
.framemap(include_bytes!("app.framemap"))
.install(|frames, info| {
// frames: Vec<Frame>, info: &PanicHookInfo
});Frame::parse works on any target (not just WASM), so you can use it server-side to process stack traces sent from browsers:
use wacks::Frame;
let frames = Frame::parse(stack_string);The WASM binary must retain its name section for function names to appear. Without it, every frame will be anonymous.
Add this to your Cargo.toml:
[profile.release]
strip = "none" # keep the name section
debug = "line-tables-only" # minimal debug info for file/line mappingIf you use wasm-bindgen, pass --keep-debug to preserve debug info through the bindgen step:
wasm-bindgen --keep-debug --target web ...| Browser | Result |
|---|---|
| Chrome | Full frames with demangled function names + WASM byte offsets |
| Firefox | Full frames with demangled function names + WASM byte offsets |
| Safari | Full frames with demangled function names + byte offsets via framemap |
The framemap is a compact build artifact generated by framemap-gen that provides:
- WebKit byte offset resolution — Safari only provides function indices in
Error.stack, not byte offsets. The framemap maps(caller, callee)function pairs to exactcallinstruction offsets. - Source location resolution — When the WASM binary contains DWARF debug info, the framemap includes a byte-offset →
(filename, line, col)table, resolving frames to real Rust source locations at runtime without external source maps.
The framemap uses delta encoding for sorted address sequences and postcard varint serialization to minimize file size.
cargo install wacks --features framemap-gen
framemap-gen input.wasm output.framemapThis requires debug = "line-tables-only" (or higher) in your release profile for source location resolution.
The caller fetches the .framemap file and passes the raw bytes to Builder::framemap:
const framemap = new Uint8Array(await (await fetch("app.framemap")).arrayBuffer());
// pass to your wasm_bindgen init function that calls Builder::framemap(&bytes)With a framemap, frames sent to error reporters contain real source locations:
{
"function": "pipeline::commit",
"filename": "src/lib.rs",
"lineno": 89,
"colno": 9,
"in_app": true
}framemap-gen— builds theframemap-genbinaryserde— derivesSerialize/DeserializeonFrame
MIT OR Apache-2.0