Either your source maps don't work, or they're publishing your source code. Most projects don't know which.
Source maps fail in two opposite directions, and both are completely silent.
They don't work. A .map that never made it into the deploy. A mappings
string that is empty. A reference the CDN stripped. Your error tracker shows a
minified stack trace, and you find out during an incident — which is the worst
possible moment to discover a debugging tool is broken.
If URLs or release names don't match, symbolication fails silently — Sentry shows the minified stack with no warning. — Sentry's own troubleshooting guide
They work too well. A .map with sourcesContent embedded, served from
your CDN, is your entire original source — comments, TODOs, internal naming —
downloadable by anyone who opens the network tab. This is not a bug in your
build. It is exactly what sourcemap: true does, which is why nothing warns
about it.
Nothing in a build, a deploy or an error tracker reports either one.
unmapped reads the directory you actually deploy and tells you which of the two you are in.
$ npm run build && npx unmapped dist
4 minified bundles · 0 will symbolicate
0%
no original source embedded in any map
2 map file(s) · treated as publicly served
error empty-mappings vendor.js.map
1 source map has an empty `mappings` string.
so: The file exists, uploads succeed, and every dashboard reports the map as
present — while symbolication resolves nothing. This is the most
misleading state of the three, because everything says it is working.
error missing-source-map app.js
1 bundle points at a source map that is not in this output.
so: The browser and your error tracker both request that file and get a 404.And the other direction:
$ npx unmapped dist
1 minified bundle · 1 will symbolicate
██████████████████████████████ 100%
249 B of original source embedded in 1 map(s)
error source-published app.js.map
249 B of original source is embedded in 1 published source map.
so: Anyone who opens the network tab can download your unminified source,
including comments and any secret that was ever written into a client
file. Search engines index .map files; so do scanners.Note the second one symbolicates perfectly. That is the problem.
npx unmapped distOr as a dev dependency:
npm install --save-dev unmappedRequires Node 20.10 or newer. Zero dependencies.
Run it on the directory you actually deploy, after the build:
npm run build && npx unmapped distunmapped dist --private # not served publicly; embedded source is expected
unmapped dist --verbose # list every bundle and map
unmapped dist --json # machine-readable
unmapped dist --fail-on warningIn CI:
- run: npm ci
- run: npm run build
- run: npx unmapped distExit codes: 0 clean, 1 findings at or above the threshold, 2 bad usage.
Is this directory served publicly?
unmapped cannot tell by looking. A dist bundled into a server image and a
dist uploaded to a CDN are the same files. So --private is an explicit
switch, and it flips the disclosure rules from findings into information.
Default is public, because that is the common case for a directory someone points this at — and because the safe default for a disclosure check is to report rather than stay quiet.
| Rule | Severity | Meaning |
|---|---|---|
missing-source-map |
error | a bundle points at a .map that is not in the output — a 404 |
empty-mappings |
error | the map exists and maps nothing |
unreadable-source-map |
error | the .map is not valid source-map JSON |
source-published |
error* | original source is embedded in a publicly served map |
no-source-map |
warning | a minified bundle with no reference at all |
build-path-leak |
warning* | sources contain absolute paths from the build machine |
inline-source-map |
warning* | the map is embedded in the bundle every visitor downloads |
orphan-source-map |
info | a .map nothing points at, still deployed |
no-embedded-source |
info | private build with no sourcesContent to show |
* downgraded or dropped with --private.
Details and worked examples: docs/rules.md.
Three fixtures, one for each state a build can be in:
git clone https://github.com/hamodywe/unmapped && cd unmapped
npm install
node src/cli.ts examples/broken-build # four ways to fail
node src/cli.ts examples/leaky-build # works perfectly, leaks everything
node src/cli.ts examples/good-build # silenceIt reads output, not configuration. A bundler config that says
sourcemap: true proves nothing — the map that reaches the deploy can still be
empty, absent, or stripped by a CDN's auto-minify. Only the emitted files
settle it.
The report never reproduces the source it is warning about. A finding that pasted your leaked code into a build log would be a strange way to warn you about leaked code. There is a test for it.
A denominator of zero is not 100%. A build with nothing minified has no symbolication question to answer, and the report says so rather than drawing a full green bar.
Zero dependencies, offline, deterministic. No network, no error-tracker API, no token. Two runs over the same directory produce identical output.
- It does not verify mappings resolve to real positions. An
empty-mappingsmap is caught; a map whose segments point at the wrong lines is not. Checking that would need the original sources, which for a public build are exactly what should not be there. - It does not check your error tracker. Whether a map was uploaded to Sentry under the right release is a question only Sentry can answer. unmapped checks the half you control locally, which is where most of the failures are.
- Minification detection is a heuristic based on line length. An unminified bundle is not reported as needing a map, because its stack traces are readable anyway.
- Remote map references are not fetched. A
sourceMappingURLpointing at another host is reported as remote and left alone; following it would mean network access. - It cannot know your deploy strips
.mapfiles. If your CDN or server config excludes them, pass--private.
We upload maps to Sentry and delete them from the deploy. Will this complain?
It will report no-source-map at warning level for the minified bundles, since
from the directory's point of view they have nothing to symbolicate against.
That is the correct reading of what was deployed. Use --fail-on error — the
default — and it will not fail your build.
Is publishing source maps actually bad?
Not always. Plenty of projects do it deliberately, and open-source front ends
have nothing to hide. The point is that it should be a decision. --private
records that you made one.
Does it modify anything? No. It only reads.
The most useful issue is a wrong verdict — a build reported as broken that symbolicates fine, or a clean report on a build that does not. See CONTRIBUTING.md.
MIT © hamodywe
- Troubleshooting source maps — Sentry, on silent symbolication failure
- Source Map Revision 3 specification — the
mappingsandsourcesContentfields sourceMappingURLand its security implications — MDN