Free Stack Trace Resolver
Paste a minified stack trace and the bundle’s source map to see where each frame really points, with the original source line.
What is a Stack Trace Resolver?
Production JavaScript is minified, so its stack traces point at line 1, column 30 of a file nobody can read. This resolver applies the bundle’s source map to each frame and gives back the original file, line and column.
It reads V8 traces from Chrome, Edge and Node as well as Firefox and Safari traces, uses Mozilla’s source-map library for the lookup, and rewrites the trace in its own style. Frames from other files and runtime code are left as written, so the trace stays complete.
Key Features
Original Positions
Each frame is rewritten to the original source file, line and column, ready to paste into an issue or open in your editor.
The Line That Threw
Source context shows the original line under each frame with a caret at the column, read from the map’s embedded sources.
Every Browser’s Traces
Chrome, Edge and Node frames in the “at” style and Firefox and Safari frames in the “@” style are both read.
Honest About Gaps
Frames from vendor files, runtime code and positions the map doesn’t cover are labelled rather than guessed at.
Private Source Maps
The map and trace are processed in your browser, so your original source code isn’t uploaded anywhere.
Perfect for everyone
- Front-end and Node developers triaging production errors
- Support engineers reading bug reports
- Anyone handed a minified trace
How to Use the Stack Trace Resolver
Paste the trace, add the map for the same build, and read the original positions.
Paste the Stack Trace
Copy the error and its frames from the console, an error tracker or a bug report.
Add the Source Map
Paste the .map file or open it with the button. It must come from the exact build that threw.
Read the Resolved Stack
Each frame now points at the original file and position. The table shows the generated and original positions side by side.
See the Source Context
Switch to Source context to see the original line under each frame, with a caret at the failing column.
Minified Traces, Resolved
Each row is a stack trace and, below the dashes, the source map it was resolved with. The map comes from esbuild minifying a four-line price function, and every answer is the resolver’s output.
“RangeError: qty must be positive at n (https://example.com/price.min.js:1:30) at HTMLButtonElement.onclick (https://example.com/cart:12:9) --- {"version":3,"file":"price.min.js","sources":["src/price.ts"],"sourcesContent":["export function price(qty: number, unit: number) {\n if (qty < 0) throw new RangeError('qty must be positive');\n return qty * unit;\n}\n"],"names":["price","qty","unit"],"mappings":"AAAO,SAASA,EAAMC,EAAaC,EAAc,CAC/C,GAAID,EAAM,EAAG,MAAM,IAAI,WAAW,sBAAsB,EACxD,OAAOA,EAAMC,CACf"}”
“RangeError: qty must be positive at n (https://example.com/price.min.js:1:30) at HTMLButtonElement.onclick (https://example.com/cart:12:9) --- {"version":3,"file":"price.min.js","sources":["src/price.ts"],"sourcesContent":["export function price(qty: number, unit: number) {\n if (qty < 0) throw new RangeError('qty must be positive');\n return qty * unit;\n}\n"],"names":["price","qty","unit"],"mappings":"AAAO,SAASA,EAAMC,EAAaC,EAAc,CAC/C,GAAID,EAAM,EAAG,MAAM,IAAI,WAAW,sBAAsB,EACxD,OAAOA,EAAMC,CACf"}”
“RangeError: qty must be positive n@https://example.com/price.min.js:1:30 onclick@https://example.com/cart:12:9 --- {"version":3,"file":"price.min.js","sources":["src/price.ts"],"sourcesContent":["export function price(qty: number, unit: number) {\n if (qty < 0) throw new RangeError('qty must be positive');\n return qty * unit;\n}\n"],"names":["price","qty","unit"],"mappings":"AAAO,SAASA,EAAMC,EAAaC,EAAc,CAC/C,GAAID,EAAM,EAAG,MAAM,IAAI,WAAW,sBAAsB,EACxD,OAAOA,EAAMC,CACf"}”
“RangeError: qty must be positive at n (https://example.com/price.min.js:1:30) at HTMLButtonElement.onclick (https://example.com/cart:12:9) --- {"version":3,"file":"price.min.js","sources":["src/price.ts"],"names":["price","qty","unit"],"mappings":"AAAO,SAASA,EAAMC,EAAaC,EAAc,CAC/C,GAAID,EAAM,EAAG,MAAM,IAAI,WAAW,sBAAsB,EACxD,OAAOA,EAAMC,CACf"}”
“RangeError: qty must be positive at n (file:///srv/app/price.min.js:1:30) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) --- {"version":3,"file":"price.min.js","sources":["src/price.ts"],"sourcesContent":["export function price(qty: number, unit: number) {\n if (qty < 0) throw new RangeError('qty must be positive');\n return qty * unit;\n}\n"],"names":["price","qty","unit"],"mappings":"AAAO,SAASA,EAAMC,EAAaC,EAAc,CAC/C,GAAID,EAAM,EAAG,MAAM,IAAI,WAAW,sBAAsB,EACxD,OAAOA,EAAMC,CACf"}”
“RangeError: qty must be positive at n (https://example.com/price.min.js:3:1) --- {"version":3,"file":"price.min.js","sources":["src/price.ts"],"sourcesContent":["export function price(qty: number, unit: number) {\n if (qty < 0) throw new RangeError('qty must be positive');\n return qty * unit;\n}\n"],"names":["price","qty","unit"],"mappings":"AAAO,SAASA,EAAMC,EAAaC,EAAc,CAC/C,GAAID,EAAM,EAAG,MAAM,IAAI,WAAW,sBAAsB,EACxD,OAAOA,EAAMC,CACf"}”
“RangeError: qty must be positive at n (https://example.com/price.min.js:1:30) --- {"version":2,"file":"price.min.js","mappings":""}”
“RangeError: qty must be positive at n (https://example.com/price.min.js:1:30) --- {"version": 3, "mappings": "AAAA",}”
“RangeError: qty must be positive --- {"version":3,"file":"price.min.js","sources":["src/price.ts"],"sourcesContent":["export function price(qty: number, unit: number) {\n if (qty < 0) throw new RangeError('qty must be positive');\n return qty * unit;\n}\n"],"names":["price","qty","unit"],"mappings":"AAAO,SAASA,EAAMC,EAAaC,EAAc,CAC/C,GAAID,EAAM,EAAG,MAAM,IAAI,WAAW,sBAAsB,EACxD,OAAOA,EAAMC,CACf"}”
“RangeError: qty must be positive at n (https://example.com/price.min.js:1:30) --- {"name": "price", "version": "1.0.0"}”
What’s Inside a Source Map
A version 3 source map is a JSON file with a handful of fields. These ten explain what each one does and which ones the lookup needs.
version
Always 3 for current tools. Older version 1 and 2 maps used a different encoding and aren’t read.
“Which version do bundlers write today?”
file
The generated file the map belongs to, such as app.min.js. When it’s set, only frames from that file are resolved.
“What does the file field name?”
sources
The original files, listed by path or URL. Bundlers often prefix them with webpack:// or a relative path.
“Where do original file names come from?”
sourceRoot
An optional prefix added to every entry in sources. The resolver applies it before showing a path.
“Is sourceRoot required?”
sourcesContent
The original source text, one string per entry in sources. Without it, positions resolve but no source line can be shown.
“Which field lets the tool show the line that threw?”
names
Identifiers referenced by mappings, such as original variable and function names that the minifier shortened.
“Why might a mapping carry a name?”
mappings
Base64 VLQ segments, grouped by generated line with semicolons and separated by commas, each linking a generated column to an original position.
“What separates generated lines in mappings?”
sections
An index map lists other maps with offsets instead of one mappings string. The resolver reads both forms.
“What replaces mappings in an index map?”
ignoreList
Indexes of sources that debuggers should hide, usually third-party code. Chrome DevTools uses it to collapse library frames.
“What does ignoreList usually mark?”
sourceMappingURL
Not a field but the comment at the end of a bundle that points to its map. Hidden source maps leave it out so browsers don’t fetch them.
“How does a browser find a bundle’s map?”
Stack Trace Formats by Runtime
Each engine writes frames its own way. These are the formats the resolver reads and what to watch for.
Chrome and Edge
V8 writes “at name (url:line:column)”, or “at url:line:column” for anonymous code. Lines and columns start at 1.
Node.js
The same V8 style, with file:// URLs for ES modules and node:internal frames for the runtime, which are kept as written.
Firefox
SpiderMonkey writes “name@url:line:column”, with an empty name for top-level code. Columns also start at 1.
Safari
JavaScriptCore uses the same “@” style, writes “global code” for top-level frames and “[native code]” for built-ins.
Async Frames
V8 prefixes awaited callers with “async”, as in “at async loadCart”. They resolve like any other frame.
Error Trackers
Sentry, Bugsnag and similar tools usually show the raw trace too. Copy that version when the tracker couldn’t find a map.
Getting a Source Map From Your Build
Most bundlers write maps with one setting. Keep them for every release, even if you don’t serve them.
esbuild
Pass --sourcemap to write app.js.map next to the bundle, or --sourcemap=external to leave out the comment that points to it.
webpack
Set devtool to source-map for full maps, or hidden-source-map to write them without the comment, which keeps them off the public site.
Vite and Rollup
Set build.sourcemap to true or hidden in Vite, or output.sourcemap in Rollup. Vite builds don’t write maps by default.
TypeScript
Set sourceMap in tsconfig.json for tsc output. Add inlineSources to embed the original code in the map.
Next.js
Set productionBrowserSourceMaps to true for client bundles. Server maps are written for Node when you enable them.
Keep Maps per Release
Store each build’s maps with the release version, so you can resolve a trace from a version that’s no longer deployed.
When to Resolve a Stack Trace
Whenever a minified trace reaches you without its sources.
Triaging a Bug Report
A user pastes a console error from production. Resolve it with that release’s map to find the failing line.
When the Error Tracker Missed
If maps weren’t uploaded for a release, resolve its traces here with the maps from your build artefacts.
Hidden Source Maps
Sites that don’t publish maps can still resolve traces privately, with the maps kept in CI.
Checking a Build’s Maps
Throw a test error and resolve it to confirm your bundler is writing correct maps before release.
Reading Node Production Logs
Server bundles built with esbuild or webpack log minified traces too, which resolve the same way.
Learning How Source Maps Work
See generated and original positions side by side and how columns shift between them.
Source Map Pitfalls
When a resolved frame points at the wrong line, one of these is usually the cause.
A Map From a Different Build
Positions only line up with the exact build that threw. A map from yesterday’s deploy gives plausible but wrong lines.
Columns Counted Differently
Stack traces count columns from 1 and source maps from 0. This resolver converts between them, but hand-rolled scripts often don’t.
Names Aren’t Function Names
A mapping’s name is the identifier at that exact spot, often the function being called, not the function the frame is in.
Inline Maps
A sourceMappingURL of data:application/json;base64 embeds the map in the bundle. Decode the base64 part before pasting it here.
Missing Original Source
Maps built without sourcesContent resolve positions but can’t show the line. Turn on inline sources in your build to include it.
Multi-Step Builds
If TypeScript output is minified again later, the final map must chain both steps. Most bundlers do this, while custom pipelines may not.
Frames From Other Bundles
A trace often crosses vendor and app bundles. Resolve each bundle’s frames with its own map.
Inlined Functions
Minifiers inline small functions into their callers, so a frame may resolve inside a function you didn’t expect.