Source Maps

How to Debug Minified JavaScript Errors

A minified stack trace is unreadable without source maps. Learn how to decode production JavaScript errors and get back to the original line.

Production JavaScript is minified — variable names become single letters, whitespace disappears, and dozens of functions collapse into a single line. When something breaks in the wild, you're left staring at stack traces pointing at index-4f9a.js:1:52210 with absolutely no way to know what code actually failed. Debug minified JavaScript errors requires source maps, a bridge that maps minified code back to your original source.

This guide explains why minified stack traces are unreadable, how source maps solve the problem, and how to set them up so every production error points back to the exact line you wrote.

Why minification makes debugging impossible

Minification is essential for production performance. By shrinking your bundle size, you ship JavaScript faster to users. But the trade-off is readability.

When you minify, the bundler rewrites your code:

// Before minification
function fetchUserData(userId) {
  const response = await fetch(`/api/users/${userId}`);
  const data = response.json();
  return data.profile;
}

// After minification
function a(b){return c=await fetch(`/api/users/${b}`),d=c.json(),d.profile}

Now imagine fetchUserData throws an error. The minified stack trace says:

TypeError: Cannot read properties of undefined (reading 'profile')
  at a (index-4f9a.js:1:12340)

That's it. The function name a tells you nothing. The line and column numbers point into bundled code where dozens of functions are squashed together. You have no idea which function failed, let alone why. You'd have to manually reverse-engineer the stack trace by searching the minified bundle for line 1, column 12340 — and even then you're staring at obfuscated variable names.

Production minification is non-negotiable for performance, but it's also non-negotiable to keep your source maps. If you have one without the other, you've optimized shipping at the cost of debugging.

What source maps do (and don't)

A source map is a file that acts as a lookup table. It records the mapping between minified code and your original source: "minified column 52210 on line 1 came from line 47 of fetchUserData in api.ts."

When an error lands in your error tracker with a minified stack trace, LightTrace reads the source map and re-maps the frame:

// What you see in LightTrace:
TypeError: Cannot read properties of undefined (reading 'profile')
  at fetchUserData (api.ts:47:21)

The function name is readable. The file path is correct. The line number points to the exact code you wrote. Now you can fix it.

Source maps are small companion files, typically named main.js.map or bundle-abc123.js.map. They're not your actual source code — they don't include the .ts or .tsx files. Instead, they're a binary format (usually JSON) that records thousands of tiny mappings. GitHub source links use source maps too, letting you jump from an error straight to the offending line on GitHub.

Source maps should never be shipped to your users' browsers. They're only for error trackers and developers. Upload them to your error tracker in CI, keep them out of your public bundle, and you get all the debugging power with zero performance cost.

How LightTrace automatically de-minifies your stack traces

LightTrace is Sentry-SDK-compatible, which means it speaks the same language as Sentry. When you upload source maps via CI or the API, LightTrace stores them and uses them to un-minify every stack trace that arrives.

Here's the flow:

  1. Build time — your bundler generates source maps (Webpack, Vite, esbuild, etc.)
  2. CI/deploy — you upload the source maps to LightTrace using the release API
  3. Runtime — an error occurs and the minified stack trace is sent to LightTrace
  4. Lookup — LightTrace maps the frame using your source map and shows you the readable stack trace

You never touch the minified source. LightTrace handles the mapping server-side, instantly turning useless stack traces into code you can actually understand.

The upload-source-maps guide walks through the mechanics of CI integration. If you're on Vite, Next.js, or Webpack, each has its own setup guide tailored to how those bundlers generate maps.

Common patterns that break source map lookup

Even with source maps uploaded, a few things can go wrong.

Mismatched filenames. If your build generates main-abc123.js but your source map is named main.js.map, LightTrace can't match them. Release tracking (tagging every event with the exact git commit or version) solves this — it lets LightTrace group source maps by release instead of relying on filenames.

Missing inline source. By default, source maps include only mappings, not the original source code. If you're uploading the .map files separately, LightTrace can't render the full stack trace context. Many teams solve this by uploading .map files with --rewrite to include inline source, or by using LightTrace's release tracking.

Source maps in development vs production. During development, it's fine to ship source maps to the browser for DevTools. In production, exclude them from your bundle and upload them separately to your error tracker. This keeps your users' browsers fast and your stack traces readable.

Best practices: source maps in your build pipeline

To debug minified JavaScript reliably:

1. Enable source maps in production builds, not dev builds. In Vite:

// vite.config.ts
export default defineConfig({
  build: {
    sourcemap: process.env.NODE_ENV === "production" ? true : false,
  },
});

2. Upload them in CI as part of your release. Use the Sentry SDK's release API to upload maps for each deploy:

npx @sentry/cli releases files <release-name> upload-sourcemaps \
  --org my-org \
  --project my-project \
  dist

3. Tag events with the exact release. When you initialize your SDK, include the release so errors can be matched to their source maps:

Sentry.init({
  dsn: "https://<key>@your-lighttrace-host/1",
  release: `web@${process.env.BUILD_COMMIT}`,
});

4. Test the mapping locally. Build for production and run your app. Throw a test error. If the stack trace shows minified code instead of your real function names, your source maps aren't being uploaded correctly.

5. Keep source maps out of your bundle. Add .map files to your .gitignore and exclude them from your production build output. They should only live on your CI server and in your error tracker's storage.

Modern bundlers make this straightforward. JavaScript error monitoring teams that skip source maps are just making their on-call harder — every production crash becomes a debugging session instead of a one-line fix.

Start tracking errors in minutes

Enable source maps, upload them to LightTrace, and see minified errors as readable stack traces pointing to the exact line you wrote.

Once your source maps are in place, every error trace becomes actionable. You'll spend minutes tracking down production bugs instead of hours squinting at minified column numbers. It's the difference between debugging blind and having the full picture.

Fix your next production error faster

Point any Sentry SDK at LightTrace — free up to 5,000 events/month.