Readable stack traces
Production builds are minified, obfuscated or stripped, so their stack traces point at code nobody wrote. Upload what your build produces and Zharp shows errors with your original functions, files and lines — and groups them by those.
What to upload, per platform
| Platform | What the build makes | How it gets to Zharp |
|---|---|---|
| Browser | Source maps | A bundler plugin (Vite, webpack, Rollup, esbuild, Next.js) or the CLI |
| Node.js | Source maps of bundled server code | The same plugins or the CLI |
| Android | mapping.txt from R8/ProGuard | The Gradle plugin io.zharp.android-gradle |
| Flutter | Dart symbols from --split-debug-info | zharp dart-symbols upload |
| iOS | dSYMs | An Xcode Run Script build phase (or curl) |
| Python, Go, PHP | — | Nothing: their stacks are readable as sent |
Once uploaded, an error is symbolicated as it arrives, before it is grouped into an issue: the issue page shows the original frames, with the code around each one where the source map includes the source, and a Raw view of the stack as it was sent.
How builds are matched
Every upload is filed under a debug ID — an identity of the exact build, not its release name. JavaScript gets a UUID per file, written into the file and its map by the plugin or CLI; Android gets a UUID per build from the Gradle plugin; Flutter and iOS use the build ID and Mach-O UUID their compilers already write. The SDK sends the IDs of the code in each error's stack, so the right files are found whatever URL, CDN or device the code ran on, and rebuilding the same release twice can't mix them up. A release on the upload is optional and only for your own filtering.
The upload key
- 1Create a key with artifacts:write
Under Integrations & API → Main API keys, create a workspace key with the Build uploads scope (
artifacts:write). This is a secret for your CI — not thezpk_app key your SDK sends data with. - 2Give it to your build
Store it in your CI's secrets as
ZHARP_AUTH_TOKEN. Never commit it, and never put it in an app. Without it, the plugins still prepare the build but upload nothing. - 3Tell it which app
Uploads go to one app, by its ID — the number in the app's dashboard URL — as
app/--apporZHARP_APP.
Seeing your uploads
- The app's Build files tab lists them — name, kind, debug ID, release, size and when it was uploaded — and lets you delete one.
- Build files are kept until you delete them, so errors from old builds stay readable. Deleting the app deletes them.
- Uploading the same file for the same build again replaces it. Up to 200 MB a file.
Troubleshooting
A stack still minified or obfuscated? Check, in order:
- Is it the first error of a new build? One that arrives before its upload can come in raw; it becomes readable when you open its issue, and the errors after it are readable as they arrive. Upload before you deploy to avoid it.
- Did the upload run? Look for it in the app's Build files tab, and in the build log: a missing
ZHARP_AUTH_TOKENmakes the plugins warn and skip the upload, a key withoutartifacts:writeor a wrong app ID fails with what to fix. - Is it the same build? Matching is by debug ID, so the files you upload must come from the build you ship. JavaScript you deploy without running
inject(or the plugin) has no debug ID; an Android build without the Gradle plugin has no UUID; a Flutter app built again after uploading has new symbols. - Is the SDK current? The SDK version that sends debug IDs is needed in the app. Old errors sent before it stay raw.
- iOS: is Debug Information Format set to DWARF with dSYM File for Release? Without it there is no dSYM to upload.