Skip to content

Turbopack dev: a route handler compiled after N pages costs ~16 MB × N (50 pages + 34 handlers → 29 GB); regressed in 16.3.0-canary.101 #98707

Description

@danielcizin

Link to the code that reproduces this issue

https://github.com/danielcizin/next-16-3-route-handler-memory-repro

To Reproduce

  1. npm install && node gen.mjs (writes 50 trivial app/p*/page.tsx and 34 trivial app/api/h*/route.ts; TypeScript 5 is pinned).
  2. node drive.mjs pages-first — fresh .next, starts next dev, GETs every page, then GETs every route handler, printing the dev server's RSS (the process listening on the port) every 10 routes.
  3. node drive.mjs handlers-first — the same with the handlers requested before the pages.

Current vs. Expected behavior

Current (next@16.3.4, also 16.3.5): after the 50 pages are compiled (~11 MB each, RSS 1.1 GB), each route handler compiled afterwards adds ~820 MB — RSS goes 1.1 → 9.2 → 17.4 → 25.7 → 29.0 GB across the 34 handlers, in 2.9 s. Requested before the pages, the same 34 handlers add ~9 MB each and the whole run ends at 1.4 GB.

The per-handler cost is linear in the number of pages already compiled: 10 pages → ~170 MB per handler, 25 → ~410 MB, 50 → ~820 MB. Removing the one shared import from the handlers (HANDLER_IMPORT=0) or from the pages (PAGE_IMPORT=0) changes nothing (28.9 GB / 27.2 GB).

Expected: a route handler's compile cost independent of how many pages were compiled before it, as in 16.2.6, where the same pages-first run ends at 2.1 GB (handlers ~9 MB each).

Bisected in the same harness (PAGES=10 HANDLERS=34, pages first; good ≈ 1 GB, bad ≈ 6.3 GB): 16.2.6, 16.3.0-canary.0/.53/.80/.93/.96/.99/.100 good; 16.3.0-canary.101 bad; 16.3.0 bad. That canary's release notes are four Turbopack server-HMR changes: #95546, #95795, #95661, #94948.

Where it bit: a CI browser matrix whose warmup requests 52 pages and then 33 route handlers of a real app killed 7 GB and 16 GB GitHub-hosted runners the moment the handlers were requested (dev server 5.9 → 8.9 → 11.7 → 14.3 GB across three handlers); reordering the warmup to handlers-first restored a 1.4 GB run. None of experimental.turbopackMemoryEviction: 'full', turbopackFileSystemCacheForDev: false, turbopackSourceMaps: false, or devMemoryThresholdRestart: false changed the peak.

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 25.5.0: Mon Apr 27 20:41:12 PDT 2026; root:xnu-12377.121.6~2/RELEASE_ARM64_T6050
  Available memory (MB): 131072
  Available CPU cores: 18
Binaries:
  Node: 26.0.0
  npm: 11.16.0
  Yarn: N/A
  pnpm: N/A
Relevant Packages:
  next: 16.3.4
  eslint-config-next: N/A
  react: 19.2.4
  react-dom: 19.2.4
  typescript: 5.9.3
Next.js Config:
  output: N/A

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

Turbopack, Route Handlers, Performance

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

next dev (local)

Additional context

Full measurement tables (both orders, three versions, the scaling series, and the bisect) are in the reproduction's README. Measured on macOS arm64 with 128 GB RAM so the run completes; on smaller machines the process is killed once the handlers start compiling. turbopackMemoryLimit (accepted by the 16.2 binding) is no longer present in 16.3's binding, so there is no bound to fall back on.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    PerformanceAnything with regards to Next.js performance.Route HandlersRelated to Route Handlers.TurbopackRelated to Turbopack with Next.js.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions