vercel / vercel/next.js

next dev hangs forever (no error, no overlay) when a module request exceeds ~8.39M chars — bundled enhanced-resolve predates the non-regex fast path

Open
#97,371 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Webpack
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/MuzajsArcin/nextjs-dev-hang-repro

To Reproduce
  1. git clone https://github.com/MuzajsArcin/nextjs-dev-hang-repro
  2. cd nextjs-dev-hang-repro && npm install
  3. node generate.mjs # writes src/big.mjs — one 13,280,481-char data: URI
  4. npm run dev # webpack dev server
  5. curl --max-time 60 http://localhost:3247/ -> never responds

Pages Router, empty next.config.mjs, no custom webpack config. src/big.mjs is a single line:

export const wasmUrl = new URL("data:application/wasm;base64,AAAA…", import.meta.url).href;

The size boundary is the clearest signal — same input, one character apart:

node generate.mjs 8388572 && npm run dev   # HTTP 500, ordinary error overlay
node generate.mjs 8388573 && npm run dev   # hangs forever

Turbopack is unaffected: npm run dev:turbo returns HTTP 200 in ~1s.

Current vs. Expected behavior

Current: compilation never finishes and the request never resolves. curl exits 28; the browser spins indefinitely. The dev server prints only:

 ○ Compiling / ...
RangeError: Maximum call stack size exceeded
    at RegExp.exec (<anonymous>)
 ⨯ unhandledRejection: RangeError: Maximum call stack size exceeded
    at RegExp.exec (<anonymous>)

Expected: either it compiles (Turbopack does, in ~1s), or it fails with an error overlay naming the module. A RangeError during compilation should not leave the request permanently unresolved.

Two things make this unusually expensive to debug:

  1. The single at RegExp.exec frame is not a truncated trace — the overflow happens inside V8's regexp engine, so no JS frame points at the offending module. --stack-trace-limit does not help, and neither does --stack-size (tried up to 60 MB): this is V8's RegExp stack cap, not the JS call stack.
  2. Because it surfaces as unhandledRejection rather than a compilation error, there is no overlay, no failed build and no module path — just an infinite hang.

I located it by preloading a patched RegExp.prototype.exec via NODE_OPTIONS --require and logging this.source plus a fresh new Error().stack from inside the catch (the regexp frames have unwound by then, so the caller chain is visible).

Root cause

parseIdentifier in the bundled enhanced-resolve runs PATH_QUERY_FRAGMENT_REGEXP on every module request. V8 throws RangeError once the subject exceeds its RegExp stack limit. Bisected to the character on Node 22.23.2 / V8 12.4.254.21-node.56: the largest input that still succeeds is 8,388,572 characters — 36 below 2^23 — identical for "A".repeat(n) and for a data: URI of the same length.

This is already fixed in enhanced-resolve itself. Standalone 5.22.0 skips the regex unless the identifier actually contains \0:

const firstEscape = identifier.indexOf("\0");
if (firstEscape !== -1) { /* regex path */ }
// otherwise: linear indexOf scans, no regex at all

But next/dist/compiled/webpack/bundle5.js still carries the older version:

const I=/^(#?(?:\0.|[^?#\0])*)(\?(?:\0.|[^#\0])*)?(#.*)?$/;
function parseIdentifier(v){const P=I.exec(v); ... }

(No firstEscape, no dosPrefixEnd — confirmed by string-searching the bundle.)

Driving webpack 5.107.2 + enhanced-resolve 5.22.0 through the Node API over the identical generated module: 0 errors, build completes. So plain webpack is fine; only the vendored copy is affected.

Suggested fixes

  1. Refresh the bundled enhanced-resolve in next/dist/compiled/webpack to a version with the \0 fast path. This alone removes the failure.
  2. Do not let a compiler RangeError become an infinite hang. Even after (1), any future unhandledRejection inside compilation should reject the pending request with an error overlay rather than leaving it open forever. That is the part that cost the most debugging time.

Where this came from (not synthetic)

@mlightcad/cad-simple-viewer ships an Emscripten SINGLE_FILE=1 worker (dist/libredwg-parser-worker.js) with a ~10 MB .wasm inlined as new URL("data:application/wasm;base64,…", import.meta.url). Any Next.js app that pulls that package into a route's module graph loses webpack dev for that route entirely. In our app three route families died at once while next build stayed green, which made it look like an app bug rather than a toolchain one. Reported to that package as well: https://github.com/mlightcad/cad-viewer/issues/494

Note

Still reproduces on next@canary 16.3.1-canary.16 (run with --webpack, since Turbopack is the dev default in 16). Turbopack is unaffected in both dev and build, on 15 and on canary.

Earlier attempts #97364 and #97365 were auto-closed by the bot because I filed them through the gh CLI rather than this form, so the reproduction link was not detected. Same report, now filed properly.

Provide environment information
Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 25.6.0
  Available memory (MB): 24576
  Available CPU cores: 10
Binaries:
  Node: 22.23.2
  npm: 10.9.8
  Yarn: N/A
  pnpm: 11.10.0
Relevant Packages:
  next: 15.5.18 (also reproduced on 16.3.1-canary.16 with --webpack)
  eslint-config-next: N/A
  react: 18.3.1
  react-dom: 18.3.1
  typescript: N/A
Next.js Config:
  output: N/A
Which area(s) are affected? (Select all that apply)

Webpack

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

next dev (local)

Additional context

Reproduces locally on macOS 26.6.1 (arm64), Node 22.23.2. Not platform-specific as far as I can tell — the limit is V8's RegExp stack, which any Node 22 build shares.

Only the webpack dev server is affected. next build (Turbopack) and next dev --turbopack are both fine, on 15.5.18 and on 16.3.1-canary.16.

The reproduction repo pins next@15.5.18 so the failure shows with a plain npm run dev. To check canary, install next@canary and run with --webpack (Turbopack is the dev default from 16 on).

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 reproduction repository and compare the webpack dev behavior at the two documented input sizes. Then inspect next/dist/compiled/webpack/bundle5.js and compare its enhanced-resolve parseIdentifier implementation with the described standalone version. Done means the webpack dev request no longer hangs for the large module and compilation failures surface as an error instead of an unresolved request.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, nextjs, node.js, webpack
Domain
build-system, devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.