1 unstable release
Uses new Rust 2024
| 0.1.0 | Mar 31, 2026 |
|---|
#424 in WebAssembly
72KB
1.5K
SLoC
wacks
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.
Usage
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);
Build configuration for useful stack traces
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 mapping
If you use wasm-bindgen, pass --keep-debug to preserve debug info through the bindgen step:
wasm-bindgen --keep-debug --target web ...
Browser support
| 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 |
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.
Generating framemaps (framemap-gen)
cargo install wacks --features framemap-gen
framemap-gen input.wasm output.framemap
This requires debug = "line-tables-only" (or higher) in your release profile for source location resolution.
Loading at runtime
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)
Result
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
}
Features
framemap-gen— builds theframemap-genbinaryserde— derivesSerialize/DeserializeonFrame
License
MIT OR Apache-2.0
Dependencies
~0.1–2MB
~39K SLoC