Source maps not resolving
You uploaded the maps, the upload script said OK, but the dashboard still shows minified frames. Here's the matching contract and how to find which side is broken.
See the source-maps setup guide for the happy path. This page is for when the setup is in place but the dashboard still shows symbols like aF / iX / oW instead of real function names.
The matching contract
The resolver pairs an event's stack frames with a stored map by looking up:
R2 key: <project_id>/<event.release>/<basename(frame.filename)>.mapThree pieces have to line up: the release tag on the event (set in Sentry.init), the release tag at upload (passed to your CI script), and the basename of the file in the stack frame. If any one of those mismatches, the resolver silently falls back to the minified frame. Below: the failure modes, ranked by frequency.
Diagnostic 1: read the event payload
Open any unresolved issue and click Copy for LLM to dump the event JSON, or use the API:
curl https://glitchreplay.com/api/v1/issues/<issue_id> \
-H "Authorization: Bearer grt_…" | jq '.latest_event.release, .latest_event.exception.values[0].stacktrace.frames[0].filename'You're looking for two things: a release that isn't empty or "dev", and a filename that ends in something like chunks/4bd1b696-…js. If release is missing or "dev", jump to the next section.
Cause 1: release tag missing or wrong on the event
The most common failure. The event hit ingest before your CI got around to setting NEXT_PUBLIC_RELEASE / VITE_RELEASE, or the build env var was scoped to server-side code so the browser bundle never received it.
- For Next.js, the env var must be prefixed
NEXT_PUBLIC_to reach the client. Otherwiseprocess.env.NEXT_PUBLIC_RELEASEisundefinedat build time andSentry.init({ release })defaults to nothing. - For Vite, use
defineat config time (see the source-maps guide for the snippet). - A common quick check: in browser devtools, run
JSON.parse(document.querySelector('script[type="application/json"]')?.textContent ?? 'null')or just inspect thereleasefield on the next event you send. It should match the tag you used at upload.
Cause 2: release tag on event ≠ release tag at upload
The event says release: "1a2b3c4d" but you uploaded under release=v1.4.3, or vice versa. The resolver can't find a matching map prefix and falls through.
Pick one source of truth — usually the short Git SHA from your CI — and use it everywhere:
# In CI:
RELEASE="${GITHUB_SHA:0:7}" # or CF_PAGES_COMMIT_SHA, etc.
# Build with it baked in:
NEXT_PUBLIC_RELEASE="$RELEASE" pnpm build
# Upload maps under the same key:
curl -X POST "https://glitchreplay.com/api/$PROJECT_ID/sourcemaps?release=$RELEASE" \
-F "file=@.next/static/chunks/foo.js.map"Cause 3: filename basename doesn't match
Each stack frame is a URL like https://example.com/_next/static/chunks/4bd1b696-44457c6ff72d72d1.js. The resolver strips the URL prefix and looks for a map with the matching basename plus .map. If your bundler hashes filenames differently between local builds and CI builds, the basenames won't match and resolution fails.
- Verify the
basename(frame.filename)from the event matches a real.js.mapin your build output. A different hash or prefix means a different basename. - Most often this happens when your CI uploads maps from a separate job that re-builds with different inputs (e.g., a different
NODE_ENVor a different chunk-hashing strategy). Upload from the same build job that produced the deployed bundles.
Cause 4: maps were uploaded after the event came in
The resolver runs at view time, not ingest time, so a late upload will still resolve future views — but if you fixed the upload after capturing the event, your existing dashboard tab is showing an old cached render. Refresh the issue page hard (Cmd+Shift+R) and the next render will re-resolve. If you're still stuck, click Recheck on the issue (visible when the issue has a URL captured) — it forces re-resolution.
Cause 5: hidden source maps
Some bundler configs strip the //# sourceMappingURL=… comment from the deployed .js while still emitting the .js.map file. GlitchReplay doesn't need the comment — it pairs by basename — but if you're running a Sentry-style tool that does need it, double-check your bundler isn't stripping more than you think.
Cause 6: only some frames are resolved, not all
You see real function names on the top three frames, then __webpack_require__ and minified shims below. That's normal — runtime / vendor frames don't have maps. The MAPPED pill renders per-frame, so any frame with a real path got resolved.
Last resort: re-upload and Recheck
If you're convinced the maps and release are right but the dashboard still won't resolve:
- Re-run your upload script — it's idempotent, so re-running overwrites silently.
- Capture a fresh error after the re-upload (the cleanest test:
setTimeout(() => { throw new Error("sourcemap test"); }, 0)in dev tools). - Open the new issue. If it resolves, your maps are fine — older issues will resolve on next render.
- If it doesn't resolve, the new event has a release / basename you can compare against the uploaded keys. Use the source-map validator to check the map file itself parses cleanly.
Related: Source maps setup · Source-map validator tool · Deminify a single stack trace