# Why do my production stack traces show minified code like app.min.js:1:48392, and how do I get readable ones?

Production stack traces point into minified files because your bundler renames and joins your code, and the error tracker only sees the shipped file. Fix it by generating source maps in the production build, uploading them to your tracker tied to the same release your app reports, and keeping the maps off your public server. Frames then resolve to your original file, line and function.

Canonical page: https://bugsly.dev/solutions/minified-stack-traces-source-maps. Last updated 2026-11-13. All solutions: https://bugsly.dev/solutions (list as JSON: https://bugsly.dev/solutions.json).

## Why it happens

A production build of a JavaScript or TypeScript app is rewritten before it ships. The minifier shortens variable and function names, strips whitespace and often joins the whole bundle into a few very long lines. When something throws, the browser or Node.js reports the position in that shipped file: a line, a column and a mangled name such as a or t. That is why the top frame says app.min.js:1:48392 instead of checkout.ts:87.

An error tracker receives exactly those frames. It cannot guess what the original source looked like. The translation back needs a source map: a JSON file generated by the build that records, for every position in the output, the original file, line, column and name. If the tracker has no source map that matches the exact build that threw the error, the frames stay minified.

The usual failure is a mismatch, not a missing feature. The maps were never generated because production builds default to no maps in many toolchains, or they were uploaded for a different build than the one running, or they were uploaded after the first errors arrived and were never re-applied. Stack traces from a build you have already deployed cannot be fixed retroactively for events that were stored without the map, so the order of steps matters.

## How to get readable stack traces (works with any error tracker that supports source maps)

1. **Generate source maps in the production build only.** Turn maps on for the build you deploy. In Vite set build.sourcemap to 'hidden', which writes the .map files but leaves out the sourceMappingURL comment that would invite browsers to download them (Vite documentation, read 2026-11-13). In webpack use devtool: 'hidden-source-map'. With tsc set sourceMap: true. Confirm the .map files exist in the output folder before moving on.

```
// vite.config.ts
export default defineConfig({
  build: { sourcemap: 'hidden' },
});
```

2. **Give every build a release name and report it from the SDK.** Use something unique per deploy, such as the git commit SHA. Pass the same string to the SDK as release when it initializes. The tracker uses that value to pick the matching maps for an incoming error, so a typo or a different format between upload and runtime is the most common reason maps do not apply.

```
Sentry.init({
  dsn: process.env.PUBLIC_DSN,
  release: process.env.GIT_SHA,
});
```

3. **Upload the maps from CI, after the build and before the deploy goes live.** Run the upload as a CI step on the same artifacts you deploy. With sentry-cli, sourcemaps inject adds a debug ID to each file and its map so they can be matched without relying on URLs, and sourcemaps upload sends them. Sentry's documentation says maps are only generated and uploaded for production builds and that Debug IDs are the default linking method (read 2026-11-13). Uploading before traffic reaches the new build means the first error is already readable.

```
export SENTRY_URL=https://your-tracker.example
export SENTRY_AUTH_TOKEN=<token from your tracker>
export SENTRY_ORG=<org slug>
export SENTRY_PROJECT=<project slug>
sentry-cli sourcemaps inject ./dist
sentry-cli sourcemaps upload --release "$GIT_SHA" ./dist
```

4. **Remove the maps from what you serve.** Maps contain your original source. Delete the .map files from the directory that gets deployed to the web server or CDN after the upload step, so they are only held by the tracker. Hosting them publicly works, but it hands your unminified code to anyone who asks for the file.

```
find ./dist -name '*.map' -delete
```

5. **Verify with a deliberate error.** Deploy, then trigger a known error in production or staging, for example a button that throws. Open the issue and check that the top frame shows your file name, line and original function. If it does not, compare the release string on the event with the release you uploaded, check that the upload step ran before the first request, and confirm the uploaded files carry debug IDs.

Why source maps fail to apply, and the check for each cause

| Symptom | Likely cause | How to check |
| --- | --- | --- |
| Frames still show a, t, e and app.min.js | No map was uploaded for this build | List the release's files in your tracker; the .map for the bundle should be present |
| Some frames resolve, others do not | Only the app bundle was uploaded, not vendor chunks | Upload the whole output folder, including lazy-loaded chunks |
| Frames resolve for new errors only | Maps were uploaded after the first errors arrived | Upload in CI before the deploy goes live; old events stay minified |
| Right file, wrong line | The map was built from a different commit than the code shipped | Build once, upload and deploy the same artifact; do not rebuild between steps |
| Nothing resolves in staging only | Staging uses a different release string | Print the SDK's release value and compare it with the upload command |

Compiled from the Vite and Sentry source map documentation (vite.dev, docs.sentry.io, both read 2026-11-13) and from the failure modes above.

## When to use Bugsly

Bugsly accepts the same sentry-cli commands, so the steps above work unchanged: point SENTRY_URL at https://bugsly.dev, create an organization auth token in Bugsly, then run sourcemaps inject and sourcemaps upload with your release. Bugsly also takes Android ProGuard mappings and Apple dSYMs through sentry-cli; on Apple platforms Swift symbol names stay mangled.

Once the frames are readable, the AI explanation on each issue and the get_issue_events tool both read the symbolicated frames, so an assistant sees checkout.ts:87 rather than a minified position. That is the practical gain: an assistant given a minified trace can only describe it, one given original frames can find the line in your repository.

## When Bugsly is not the right answer

If you only need to read one trace right now, you do not need to set anything up: paste it into a debugger with the map file, or use the source-map package locally. If your stack is not JavaScript, Android or Apple, source maps are the wrong tool; native crash dumps (minidumps) are not ingested by Bugsly, so use a tracker that accepts them.

## Ask your AI assistant to do it

Connect the Bugsly MCP server (https://bugsly.dev/mcp), then paste:

> Assign to Bugsly: use list_issues to find the 3 newest unresolved issues in my project, then get_issue_events on each and tell me whether the top stack frame shows my original file and line or a minified bundle. Do not resolve or ignore anything; show me what you found so I can approve the next step.

## Frequently asked questions

### Can I fix minified stack traces for errors that already happened?

Not reliably. Events stored without a matching map keep their minified frames. Upload the maps for that release anyway so new events resolve, and treat the old events as a loss. The fix is to upload before the deploy goes live next time.

### Is it safe to host source maps on my website?

It works, but anyone can then download your original source. Tracker-side upload with the .map files removed from the public output is the safer default, and it is what most tracker documentation recommends for production.

### Do I need source maps for Node.js on the server?

Only if the server runs transpiled or bundled code, for example TypeScript compiled to dist or a bundled serverless function. Plain JavaScript run as written already has readable frames. Node.js can apply maps itself with the --enable-source-maps flag, and a tracker can apply uploaded ones.

### Why does the release name matter so much?

The tracker uses the release on the event to choose which maps to apply. With debug IDs the match is by file instead, but a consistent release string still lets you see which deploy introduced an error. Use the commit SHA for both the upload and the SDK.

## Sources

- Sentry docs: Source Maps for Browser JavaScript (read 2026-11-13): https://docs.sentry.io/platforms/javascript/sourcemaps/
- Vite docs: build.sourcemap (read 2026-11-13): https://vite.dev/config/build-options#build-sourcemap

## Related

- Migrate from Sentry (includes the sentry-cli source map commands): https://bugsly.dev/migrate-from-sentry
- How to read stack traces like a senior developer: https://bugsly.dev/blog/how-to-read-stack-traces-like-a-senior-developer
- Explain any error or stack trace: https://bugsly.dev/tools/explain-error
