Quick answer: Minified web bundles and obfuscated Android builds rename your functions to short tokens so a crash trace reads like gibberish. The fix is the mapping artifact generated at build time: a source map for JavaScript or a ProGuard and R8 mapping file for Android. Archive that artifact per build, then run the trace through a retrace tool keyed to it to recover the original names, files, and lines.
If your game ships a minified web bundle or an obfuscated Android build, the crash traces you receive will be nearly useless at first glance. A function you named handlePlayerInput becomes a single letter, and a clean call chain collapses into a row of one-character tokens on one giant line. This is by design: minification shrinks code and obfuscation hides it. The recovery step is deobfuscation, and it depends entirely on keeping the mapping file the build tool produced. This post covers source maps for the web, ProGuard and R8 mappings for Android, and how to run a real trace back through them.
Why traces come back minified
Web builds run through a bundler and minifier that rename every local symbol to the shortest possible token and collapse the code onto a few lines to save bytes. Android release builds run through R8, the successor to ProGuard, which renames classes and methods to short names both to shrink the app and to make reverse engineering harder. Both transformations are great for shipping and terrible for reading crashes, because the names and line numbers in the trace no longer correspond to anything you wrote.
The crucial thing to understand is that the original information is not lost, it is moved. The build tool emits a mapping that records, for every renamed or relocated symbol, what it used to be. A JavaScript source map ties minified positions back to original files and lines. An R8 mapping file ties obfuscated class and method names back to their originals. Deobfuscation is simply applying that mapping to an incoming trace, so the entire game is making sure you kept the right mapping.
Source maps for web builds
A source map is a JSON file your bundler generates that describes how each position in the minified output corresponds to a position in your original source. When a crash arrives with minified line and column numbers, a source map consumer reads those positions, looks them up, and returns the original file, function name, and line. The result is a stack trace that reads like your actual code, even though the player ran a bundle where every name was a single character.
The discipline is to generate a source map for every release and archive it keyed to that build, but not ship it publicly if you are obfuscating to protect your code. Many teams upload the source map to their error tracker as a private artifact and serve the bundle without one. The mapping must match the exact bundle the player ran, byte for byte, or the positions will resolve to the wrong lines, which is worse than no mapping at all because it looks plausible.
ProGuard and R8 mappings for Android
On Android, enabling R8 shrinking and obfuscation produces a mapping file, usually named mapping.txt, for each release build. It is a plain text dictionary from obfuscated names back to original class and method names, including line number ranges. Without it, a stack trace shows classes like a.b.c and methods like a, which tells you nothing. With it, those resolve back to your real package, class, and method names, restoring a readable trace.
Google ships a retrace tool that consumes this mapping file and an obfuscated trace and prints the deobfuscated version. The non-negotiable rule is to keep the exact mapping.txt for every release you ship, because R8 produces a different mapping on every build. Reusing a mismatched mapping silently produces wrong method names. Archive mapping.txt alongside your symbols in your release pipeline, keyed to the version, and treat losing it as seriously as losing the source for that build.
Running the trace back through retrace
With the right mapping in hand, deobfuscation is a single transformation. For Android, you feed the obfuscated stack and the matching mapping.txt into retrace and read the restored trace. For the web, you feed the minified positions and the source map into a source map consumer. In both cases the output is the trace you wanted: real names, real files, real lines. The work is finding the matching mapping for the build that produced the crash, not the resolution itself.
Automating this beats doing it by hand. A trace pasted into a command line works once, but at any real volume you want the mapping applied automatically as crashes arrive, so every trace in your tracker is already readable. The manual path is fine for a one-off investigation, but it breaks down the moment you are triaging dozens of grouped issues. Push the mapping into your crash pipeline and let deobfuscation happen on ingest, so nobody on the team ever reads raw minified output.
Setting it up with Bugnet
Bugnet captures web and Android crashes through its SDK with the full stack trace, the build version, and device context attached. You upload your source maps or R8 mapping files keyed to each release as part of your build pipeline, and Bugnet applies the matching mapping to incoming traces automatically, so the dashboard shows your original function and class names instead of minified tokens. Because the build version travels with every report, the platform always selects the correct mapping rather than guessing.
Deobfuscated traces become far more useful once Bugnet groups duplicates. The same minified crash from hundreds of players folds into a single grouped issue with an occurrence count, and that count, attached to a now-readable trace, tells you which real function to fix first. You can filter by build to confirm a crash started after a specific release, sort by occurrence to triage the worst issues, and tag each grouped trace with an owner, all from one dashboard where every trace is already deobfuscated.
Keeping mappings reliable
Deobfuscation fails quietly when mappings drift, so build guardrails into your pipeline. Generate the mapping in the same CI step that produces the release artifact, upload it immediately, and fail the build if the upload does not complete. Tag both the artifact and the mapping with the same version identifier so they can never be confused later. A missing or mismatched mapping should block a release the same way missing native symbols would.
The reward for this discipline is that crash triage on minified builds feels exactly like triage on a debug build. Your team reads real names, recognizes the code, and fixes the bug without first decoding a wall of single letters. For an indie studio shipping protected web or Android builds, that is the difference between crashes being actionable and crashes being ignored. Set the mapping pipeline up once and every future trace arrives readable.
Deobfuscation lives or dies on the mapping file. Archive a source map or R8 mapping per build and apply it on ingest.