Using Chrome DevTools Source Map Overrides
You have a symbolicated production stack frame, a one-line candidate fix in your original .tsx, and no appetite for a full deploy just to see whether it holds against the real backend. Chrome’s Local Overrides let you serve a patched copy of the deployed bundle — and its source map — to a live tab so you can prove the fix and confirm the map still resolves to the right line before you ship. This how-to is one technique within Debugging Source Maps in Browser DevTools, part of the wider Source Map Generation & Stack Trace Debugging reference. It assumes you already have a resolving map; if the map is missing entirely, start with Using Chrome DevTools to Debug Minified Bundles first.
Symptom / Trigger
Your error tracker resolves an event to a clean original coordinate, so symbolication works. But the frame points at a line you cannot safely change from your editor alone, because the bug only reproduces against production data — a specific cart shape, a feature flag, a cached response. You need to run the corrected code path in the actual tab that fails:
TypeError: Cannot read properties of undefined (reading 'total')
at checkout (webpack://app/./src/components/Cart.tsx:88:19)
at onClick (webpack://app/./src/components/Cart.tsx:142:7)
at HTMLButtonElement.<anonymous> (app.9f2c1.js:1:44820)
The deployed asset is https://cdn.example.com/assets/app.9f2c1.js, minified to one line, with //# sourceMappingURL=app.9f2c1.js.map at its tail. You want to patch line 88 of Cart.tsx as the map reports it, run it against the live checkout API, and watch a real order succeed — all without cutting a release or touching the CDN.
The distinguishing feature of this scenario is that everything about symbolication is already healthy. The map fetches with a 200, its sourcesContent carries the original TypeScript, and clicking the frame opens Cart.tsx at the correct line. What is missing is a way to execute a changed version of that line in the environment where it breaks. A local branch reproduces nothing because the failing cart never exists on your machine; a staging deploy carries different session state and a different data snapshot. The only place the bug lives is this tab, so the fix has to be tried in this tab.
Root Cause Explanation
The trap developers fall into is editing the original source shown through the map and expecting the running page to change. It will not. The browser executes the minified bytes of app.9f2c1.js; the map is a read-only overlay that translates coordinates for display. Saving an edit to the mapped Cart.tsx view without an override changes nothing that executes.
// What the tab actually runs — one minified line. The mapped Cart.tsx you SEE
// is reconstructed from app.9f2c1.js.map; editing that view alone runs nothing.
function checkout(e){return e.cart.total.toFixed(2)} // e.cart is undefined at runtime
To make a change take effect you must override the byte stream the browser fetches. Local Overrides intercept the network response for an exact URL and serve a locally stored copy instead, so a patch to the minified file — or a patch to the map — is what the runtime honours. The distinction between the map and the bytes is the whole lesson here: a map is metadata that answers “which original line did this generated position come from?”, and nothing about editing that answer alters the generated code. The bytes are the program; the map is a lookup table pointed at them. Overrides operate on the bytes, which is exactly why they change behaviour where an edit to the mapped view does not.
The pipeline below is the mental model: the request leaves the page, DevTools substitutes your saved file, and the tab executes your version while everything else stays live.
Step-by-Step Fix
- Point DevTools at a writable overrides folder outside your repo so patches never get committed.
# A scratch directory DevTools will populate with intercepted responses.
mkdir -p ~/devtools-overrides
# In DevTools: Sources > Overrides > "Select folder for overrides" > pick it, accept banner.
- Save the deployed bundle for overrides, which snapshots the exact minified bytes locally.
// Right-click app.9f2c1.js in the Sources tree > "Save for overrides".
// A purple dot now marks it; DevTools serves this local copy for that URL.
console.log('%cBundle saved for overrides', 'color:#16a34a'); // confirmation marker
- Also save the map so DevTools symbolicates the patched bytes, not the stale CDN map.
# Copy the deployed map into the override folder mirroring its URL path.
mkdir -p ~/devtools-overrides/cdn.example.com/assets
curl -s https://cdn.example.com/assets/app.9f2c1.js.map \
-o ~/devtools-overrides/cdn.example.com/assets/app.9f2c1.js.map # keep names aligned
- Patch the minified line the map pointed you to — guard the undefined access at the mapped offset.
// In the overridden app.9f2c1.js, edit the checkout body at the mapped Cart.tsx:88.
function checkout(e){return (e.cart && e.cart.total ? e.cart.total : 0).toFixed(2)} // guarded
- Reload against production and drive the exact failing path so the fix runs on real data.
// Trigger the real code path (click Checkout, or dispatch the event from the Console).
document.querySelector('[data-testid="checkout"]').click(); // hits the live backend, patched code
One nuance makes overrides worth the setup: because the interception happens before the CDN, the rest of the page — API calls, cookies, auth, feature flags — stays exactly as production serves it. You are not running a local dev build with mocked data; you are running the real deployed application with a single file swapped. That fidelity is why an override often settles “does this fix actually work here?” in one reload, where a staging redeploy would take twenty minutes and still not carry the same session state.
If you prefer to keep working in original coordinates rather than minified bytes, override the .map instead of the code: edit sourcesContent for Cart.tsx inside the saved map, and DevTools will render your edited source over the unchanged bundle. That is convenient for reading, but remember it changes only what you see — the executed bytes are still the minified original unless you also patch the code file. For a true runtime fix, the code override in step four is the one that matters.
Verification
Prove two things: the patched code path no longer throws, and the map still resolves the new bytes to a sane line. Run a scripted check in the Console after reloading with the override active.
// Paste in the DevTools Console on the reloaded, overridden page.
// 1. The guarded path must not throw on the failing cart shape.
const probe = { cart: undefined };
console.assert(typeof checkout(probe) === 'string', 'checkout still throws on undefined cart');
// 2. The override must be the file actually served (purple dot / Network "Override" column).
performance.getEntriesByType('resource')
.filter((e) => e.name.endsWith('app.9f2c1.js'))
.forEach((e) => console.log('served from override:', e.transferSize === 0)); // 0 = local copy
// 3. Force the real frame and click through — it should open your patched line, not throw.
try { document.querySelector('[data-testid="checkout"]').click(); }
catch (err) { console.error('fix did not hold:', err); }
A passing run shows the assertion succeeding, served from override: true, and a completed checkout against the live API with no TypeError in the Console. Treat each of the three checks as independent: the assertion proves the logic, the transferSize reading proves the override is the file actually in play rather than a cached CDN copy sneaking through, and the click-through proves the fix survives contact with real data. A green assertion with a non-zero transferSize is the most common false positive — it means your patched source is correct but the browser is still running the deployed bundle, so you have proven nothing about production. The before/after below captures the difference the override makes to a debugging session.
Edge Cases & Gotchas
- Overrides persist silently across reloads. The browser keeps serving your local copy on every load until you disable the Overrides toggle. Watch the purple dot on the file tab and clear the override once the session ends, or you will “reproduce” a bug that only your patched tab has.
- A patched bundle desyncs from the deployed map. If you edit code but keep the CDN map, the map’s positions no longer match your bytes and breakpoints bind to the wrong line. Always override the
.mapalongside the code, or accept that mapping is approximate while patched. - Compression and content hashes. Overridden responses are served uncompressed from disk, so
transferSizereads0and any subresource-integrity check on the bundle will fail. Disable SRI for the override session, or the browser rejects your patched file outright. - Cache masks the override. A
disk cachehit can serve the CDN copy before the override applies. Enable Network → Disable cache while DevTools is open so every reload re-runs the interception.
FAQ
Why does editing the mapped original source not change what the page runs?
Because the map is a display overlay, not the executable. The tab runs the minified app.9f2c1.js; the Cart.tsx you see is reconstructed from app.9f2c1.js.map for readability. To change runtime behaviour you must override the byte stream the browser fetches — the minified file itself — which is what “Save for overrides” and the code edit in the Step-by-Step Fix accomplish. Editing the map alone only changes what is rendered in the Sources panel.
Do I need to override the map as well as the bundle?
Only if you want symbolication to stay accurate after patching. Overriding just the code makes the fix run, but the untouched map’s line and column positions drift relative to your new bytes, so DevTools may highlight the wrong line. Saving the .map into the override folder alongside the bundle keeps the two in sync. For a broader treatment of when maps and bundles fall out of step, see Local Symbolication with the Mozilla source-map Library.
Is it safe to test against production traffic this way?
The override changes only what your own browser executes; it never touches the CDN or other users. That said, the patched code hits the live backend, so any write it performs — placing an order, mutating state — is real. Guard destructive paths behind a flag you set in the Console, or point the session at a production-like account, before you click through a mutation.
Related
- Debugging Source Maps in Browser DevTools — parent guide covering map resolution, Workspaces, and overrides end to end.
- Source Map Generation & Stack Trace Debugging — the full reference from build configuration to automated symbolication.
- Using Chrome DevTools to Debug Minified Bundles — the map-less counterpart: pretty-print and column anchoring when no map exists.
- Local Symbolication with the Mozilla source-map Library — resolving coordinates programmatically when you need to script the mapping.