nextjs - 💡(How to fix) Fix Memory leak (ArrayBuffer/WriteWrap retention) in Next.js 16.1.6 stable — fixed in canary but not released [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
vercel/next.js#90895Fetched 2026-04-08 00:19:18
View on GitHub
Comments
1
Participants
2
Timeline
6
Reactions
0
Timeline (top)
closed ×1commented ×1labeled ×1locked ×1

Root Cause

Three bugs in Next.js 16.1.6 interact to cause unbounded ArrayBuffer growth:

  1. clone-response.js: Only cloned1 is registered with FinalizationRegistry, not cloned2. Since cloned2 is stored in caches, its underlying tee buffer (backed by ArrayBuffers) is never auto-cancelled. Fixed in #88577.

  2. cache-handlers/default.js: Empty ReadableStream bodies get size = 0 in the LRU cache. Zero-size entries never trigger eviction (totalSize never exceeds maxSize), causing unbounded Map growth. Fixed in #89040.

  3. Response pipeline retention: sendResponsepipeToNodeResponse creates WriteWrap objects via res.write(chunk) that are pinned to GC roots. Without the FinalizationRegistry fix, the source ReadableStreams (tee branches) are never cancelled, keeping the write buffers alive indefinitely. Partially addressed in #90771.

Code Example

JSArrayBufferData (32 MB)
ArrayBuffer
Buffer
Array[4]
WriteWrap._chunks     ← pinned to GC roots (Global handles)

---

Platform: linux (AWS ECS Fargate, ARM64 Graviton)
Node: v22.22.0
Next.js: 16.1.6 (leaks)16.2.0-canary.79 (fixed)
output: standalone
compress: false (offloaded to ALB)
RAW_BUFFERClick to expand / collapse

Link to the code that reproduces this issue

Reproduction was done on our private production app, but the issue matches existing reports in #85914, #89033, #90433.

To Reproduce

  1. Create a Next.js app with output: 'standalone', deploy on ARM64 (AWS ECS Fargate with Graviton)
  2. Send traffic to any API routes or pages that go through the Next.js response pipeline
  3. Monitor process.memoryUsage().arrayBuffers via a health endpoint

Current vs. Expected behavior

Current (16.1.6 stable): ArrayBuffers grow linearly at ~5 MB/s and are never reclaimed, even with --expose-gc and manual global.gc(). OOM kill within 10 minutes.

Expected: Memory should stabilize and be reclaimed by GC after requests complete.

Evidence

Memory timeline on 16.1.6 (stable) — leaks

TimeRSSArrayBuffersGC ran
0s96 MB0 MB-
30s300 MB94 MBno
120s935 MB573 MByes
270s2170 MB1542 MByes
420s3183 MB2423 MByes
540s3833 MB3052 MByes → OOM

Memory timeline on 16.2.0-canary.79 — fixed

TimeRSSArrayBuffersNote
baseline326 MB32 MB
60s (under load)1556 MB1040 MBpeak during traffic
80s (load stopped)1493 MB599 MBreclaiming
120s1440 MB316 MBstill dropping
settled328 MB32 MBback to baseline

Heap dump analysis (captured at 1008 MB ArrayBuffers)

977 MB retained across 183 JSArrayBufferData objects. Retainer chains:

JSArrayBufferData (32 MB)
  ← ArrayBuffer
    ← Buffer
      ← Array[4]
        ← WriteWrap._chunks     ← pinned to GC roots (Global handles)

20 of 31 large ArrayBuffers (>30MB) retained by WriteWrap._chunks (Node.js native HTTP write wrappers). 5 of 31 retained by circular Generator chains (async stream iteration).

Root cause

Three bugs in Next.js 16.1.6 interact to cause unbounded ArrayBuffer growth:

  1. clone-response.js: Only cloned1 is registered with FinalizationRegistry, not cloned2. Since cloned2 is stored in caches, its underlying tee buffer (backed by ArrayBuffers) is never auto-cancelled. Fixed in #88577.

  2. cache-handlers/default.js: Empty ReadableStream bodies get size = 0 in the LRU cache. Zero-size entries never trigger eviction (totalSize never exceeds maxSize), causing unbounded Map growth. Fixed in #89040.

  3. Response pipeline retention: sendResponsepipeToNodeResponse creates WriteWrap objects via res.write(chunk) that are pinned to GC roots. Without the FinalizationRegistry fix, the source ReadableStreams (tee branches) are never cancelled, keeping the write buffers alive indefinitely. Partially addressed in #90771.

Fix confirmation

All three fixes are present in [email protected]. We confirmed the leak is completely resolved on canary — ArrayBuffers return to baseline (32 MB) after load stops.

However, none of these fixes are in any 16.x stable release (latest is 16.1.6). PR #89040 was backported to 15.5.11, but the 16.x branch has not received it.

Request

Please release a 16.x stable version that includes PRs #88577, #89040, and #90771. This is a critical production issue causing OOM kills for anyone using output: 'standalone' with server-side fetch on Next.js 16.

Provide environment information

Platform: linux (AWS ECS Fargate, ARM64 Graviton)
Node: v22.22.0
Next.js: 16.1.6 (leaks) → 16.2.0-canary.79 (fixed)
output: standalone
compress: false (offloaded to ALB)

Which area(s) are affected?

Performance, Runtime

Which stage(s) are affected?

Other (Deployed, production)

extent analysis

Fix Plan (short & actionable)

  1. Upgrade Next.js
    # In your repo
    npm i next@latest   # 16.2.x or newer (includes #88577, #89040, #90771)
    # or, if you must stay on 16.1.x, install the canary build
    npm i

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

nextjs - 💡(How to fix) Fix Memory leak (ArrayBuffer/WriteWrap retention) in Next.js 16.1.6 stable — fixed in canary but not released [1 comments, 2 participants]