vercel / vercel/next.js

Next.js v15.2 creates a lot of fake scripts for server components with the same source map which breaks JS debuggers

Open
#77,733 19 comments 22 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

linear: next
Dominant language
JavaScript
Stars
142k
Forks
32.4k
Avg merge
2d 14h
Merged PRs (30d)
351

Description

Link to the code that reproduces this issue

https://github.com/Soarex16/next-15-server-components-reproduction-app

To Reproduce
  1. Start the application in dev mode (either next dev or next dev --turbopack)
  2. Set a breakpoint on app/page.tsx:3 (on the console.log)
  3. Start debugging in a browser
Current vs. Expected behavior
Expected behavior

The debugger will stop in app/page.tsx only once (at the breakpoint on line 3).
After resuming the code, the debugger will not stop.

Actual behavior

The debugger stops at app/page.tsx:3, but after resume it stops at random locations in the same file.

Provide environment information
Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 24.3.0: Thu Jan  2 20:24:23 PST 2025; root:xnu-11215.81.4~3/RELEASE_ARM64_T6020
  Available memory (MB): 65536
  Available CPU cores: 12
Binaries:
  Node: 20.9.0
  npm: 10.1.0
  Yarn: 1.22.21
  pnpm: 8.10.5
Relevant Packages:
  next: 15.3.0-canary.29 // Latest available version is detected (15.3.0-canary.29).
  eslint-config-next: N/A
  react: 19.1.0
  react-dom: 19.1.0
  typescript: 5.8.2
Next.js Config:
  output: N/A
Which area(s) are affected? (Select all that apply)

Not sure

Which stage(s) are affected? (Select all that apply)

next dev (local)

Additional context
TLDR
  • React Server components machinery creates fake scripts with the same source map as a real user code to preserve source mapped traces in the browser.
    This confuses js debuggers because they think that this piece of code was bundled in different places multiple times.
    So when the user put a breakpoint, the debugger put breakpoints in all these scripts causing redundant breaks.
  • This issue affects server components debugging both Webpack and Turbopack
  • This bug was introduced somewhere between 15.1.7 and 15.2. Sorry, I didn't have enough time to bisect it.
  • Reproducible with all JS popular debuggers (Chrome DevTools, WebStorm, VS Code). What's interesting is, in Firefox it works as expected.
  • Video with debugging in Chrome DevTools:

https://github.com/user-attachments/assets/fc10b498-6972-4077-8a52-70e62f6275ab

Detailed investigation

I discovered this issue working on turbopack support in WebStorm.

First I thought that is another bug in our JS debugger, but after checking CDP logs
I noticed that Next.js creates a lot of scripts with url like rsc://React/Server/webpack-internal:///(rsc)/./app/page.tsx?SOME_NUMBER.
Here is an example of one of the scripts:

/* This module was rendered by a Server Component. Turn on Source Maps to see the server source. */






({"Home":_=>
        _()})
//# sourceURL=rsc://React/Server/webpack-internal:///(rsc)/./app/page.tsx?0
//# sourceMappingURL=http://localhost:3000/__nextjs_source-map?filename=webpack-internal%3A%2F%2F%2F%28rsc%29%2F.%2Fapp%2Fpage.tsx

All these scripts created at react-server-dom-webpack-client.browser.development.js.
Here is one of the creation traces of such fake scripts

page.tsx:5 <<< This stack frame was source mapped from the fake scripts
createFakeFunction(), react-server-dom-webpack-client.browser.development.js:1954
buildFakeCallStack(), react-server-dom-webpack-client.browser.development.js:1976
react-stack-bottom-frame(), react-server-dom-webpack-client.browser.development.js:2595
anonymous(), react-server-dom-webpack-client.browser.development.js:2331
initializeModelChunk(), react-server-dom-webpack-client.browser.development.js:1054
getOutlinedModel(), react-server-dom-webpack-client.browser.development.js:1327
parseModelString(), react-server-dom-webpack-client.browser.development.js:1540
anonymous(), react-server-dom-webpack-client.browser.development.js:2294
initializeModelChunk(), react-server-dom-webpack-client.browser.development.js:1054
resolveModelChunk(), react-server-dom-webpack-client.browser.development.js:1031
resolveModel(), react-server-dom-webpack-client.browser.development.js:1599
processFullStringRow(), react-server-dom-webpack-client.browser.development.js:2288
processFullBinaryRow(), react-server-dom-webpack-client.browser.development.js:2233
progress(), react-server-dom-webpack-client.browser.development.js:2479
Async call from Promise.then
progress(), react-server-dom-webpack-client.browser.development.js:2499
Async call from Promise.then
progress(), react-server-dom-webpack-client.browser.development.js:2499
Async call from Promise.then
progress(), react-server-dom-webpack-client.browser.development.js:2499
Async call from Promise.then
startReadingFromStream(), react-server-dom-webpack-client.browser.development.js:2506
exports.createFromReadableStream(), react-server-dom-webpack-client.browser.development.js:2718
createFromReadableStream(), app-index.tsx:157
(app-pages-browser)/./node_modules/next/dist/client/app-index.js(), main-app.js:160
options.factory(), webpack.js:700
__webpack_require__(), webpack.js:37
fn(), webpack.js:357
require(), app-next-dev.ts:9
hydrate(), app-bootstrap.ts:78
hydrate(), app-bootstrap.ts:20
loadScriptsInSequence(), app-bootstrap.ts:60
appBootstrap(), app-next-dev.ts:8
(app-pages-browser)/./node_modules/next/dist/client/app-next-dev.js(), main-app.js:182
options.factory(), webpack.js:700
__webpack_require__(), webpack.js:37
__webpack_exec__(), main-app.js:2824
anonymous(), main-app.js:2825
webpackJsonpCallback(), webpack.js:1376
anonymous(), main-app.js:9

And since all these scripts point to the same source map, this causes JS Debuggers to set redundant breakpoints.

I made some research and that's probably caused by React Server components implementation.
Since part of a component is executed on server, React is trying to recreate stack traces for some operations in a browser. One of the examples is console.log replay:

Image

Here, to show a correct source location in the console during replay on the client, React use these techniques with fake scripts and source maps.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the linked reproduction and inspect react-server-dom-webpack-client.browser.development.js, especially the fake-script creation path and its sourceURL/sourceMappingURL behavior. Reproduce under next dev with Webpack and Turbopack, then verify that the debugger stops at app/page.tsx:3 only once and does not stop again after resuming.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, next.js, react
Domain
developer-experience, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.