You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
v24.16.0, v24.17.0, and v24.18.0 (entire current 24.x line). Confirmed clean on v24.15.0 and on v22.20.0 / v22.21.0.
Platform
Reproduced on Linux under Docker on both musl (node:<version>-alpine) and glibc (debian:bookworm-slim + the official nodejs.org build), and on both arm64 and x64. The leak is libc- and arch-independent.
Subsystem
stream / webstreams / Blob
What steps will reproduce the bug?
Consuming a native-backedReadableStream — the output of Blob.prototype.stream() or CompressionStream — by draining it (e.g. new Response(stream).arrayBuffer()) leaks the native source buffer on every call. The growth is in RSS (off-heap / native); it is not visible in arrayBuffers or the V8 heap.
Minimal reproducer (no dependencies) — save as repro.js:
How often does it reproduce? Is there a required condition?
Every run, immediately. It requires a native-backed stream source (Blob.stream() or CompressionStream); a hand-written JS ReadableStream consumed the same way does not leak.
What is the expected behavior? Why is that the expected behavior?
RSS stays flat — each iteration's Blob/stream is unreachable after arrayBuffer() resolves, so the native source buffer should be released.
What do you see instead?
RSS grows without bound. Measured RSS in a tight drain loop (50 KB payload, --memory=4g):
Node
RSS
v24.15.0
~208 MB
flat
v24.16.0
~4 GB (≈4 GB within 10 s)
leak
v24.17.0
~4 GB (≈4 GB within 10 s)
leak
v24.18.0
~4 GB (≈4 GB within 10 s)
leak
v22.20.0 / v22.21.0
~130 MB
flat
The v24.18.0 row was measured on glibc/arm64 (debian:bookworm-slim, official build): RSS hit ~4 GB in ~10 s and the process was OOM-killed at ~110 k iterations, whereas v22.21.0 on the identical base ran the full window flat at ~127 MB across ~4.1 M iterations. The leak is still present in the latest 24.x (v24.18.0) — it has not been fixed on the 24.x line.
CompressionStream output (also native-backed) behaves identically. A plain JS ReadableStream consumed via the same new Response(stream).arrayBuffer() does not leak — so the leak is specific to the native source buffer, not to Response/stream consumption in general.
Additional information
This is very likely related to #63574 (“Blob.prototype.stream() leaks the source buffer on v26”, open, fix PR #63577) and #63708 (v24.16.0, closed as a duplicate of #63574). The distinction motivating this report:
The drain path (new Response(stream).arrayBuffer()), measured by RSS, already regresses across the v24.16.0 → v24.18.0 LTS line — which the arrayBuffers-based bisect does not capture.
Real-world impact: a service logging via @axiomhq/pino (which gzips each batch with new Blob([data]).stream().pipeThrough(new CompressionStream('gzip'))) leaked ~140–230 MB/day of off-heap RSS in production on Node 24.16 and eventually exhausted host RAM; pinning the runtime to Node 22 resolved it. Filing because the LTS / RSS manifestation isn't captured by #63574's current bisect and may need a backport to 24.x — happy to consolidate if maintainers consider it the same root cause.
Version
v24.16.0, v24.17.0, and v24.18.0 (entire current 24.x line). Confirmed clean on v24.15.0 and on v22.20.0 / v22.21.0.
Platform
Reproduced on Linux under Docker on both musl (
node:<version>-alpine) and glibc (debian:bookworm-slim+ the official nodejs.org build), and on both arm64 and x64. The leak is libc- and arch-independent.Subsystem
stream / webstreams / Blob
What steps will reproduce the bug?
Consuming a native-backed
ReadableStream— the output ofBlob.prototype.stream()orCompressionStream— by draining it (e.g.new Response(stream).arrayBuffer()) leaks the native source buffer on every call. The growth is in RSS (off-heap / native); it is not visible inarrayBuffersor the V8 heap.Minimal reproducer (no dependencies) — save as
repro.js:Run:
How often does it reproduce? Is there a required condition?
Every run, immediately. It requires a native-backed stream source (
Blob.stream()orCompressionStream); a hand-written JSReadableStreamconsumed the same way does not leak.What is the expected behavior? Why is that the expected behavior?
RSS stays flat — each iteration's
Blob/stream is unreachable afterarrayBuffer()resolves, so the native source buffer should be released.What do you see instead?
RSS grows without bound. Measured RSS in a tight drain loop (50 KB payload,
--memory=4g):The v24.18.0 row was measured on glibc/arm64 (
debian:bookworm-slim, official build): RSS hit ~4 GB in ~10 s and the process was OOM-killed at ~110 k iterations, whereas v22.21.0 on the identical base ran the full window flat at ~127 MB across ~4.1 M iterations. The leak is still present in the latest 24.x (v24.18.0) — it has not been fixed on the 24.x line.CompressionStreamoutput (also native-backed) behaves identically. A plain JSReadableStreamconsumed via the samenew Response(stream).arrayBuffer()does not leak — so the leak is specific to the native source buffer, not toResponse/stream consumption in general.Additional information
This is very likely related to #63574 (“
Blob.prototype.stream()leaks the source buffer on v26”, open, fix PR #63577) and #63708 (v24.16.0, closed as a duplicate of #63574). The distinction motivating this report:arrayBuffers+stream.cancel()and lands on v26 only (v24.15.0 reported clean).new Response(stream).arrayBuffer()), measured by RSS, already regresses across the v24.16.0 → v24.18.0 LTS line — which thearrayBuffers-based bisect does not capture.Real-world impact: a service logging via
@axiomhq/pino(which gzips each batch withnew Blob([data]).stream().pipeThrough(new CompressionStream('gzip'))) leaked ~140–230 MB/day of off-heap RSS in production on Node 24.16 and eventually exhausted host RAM; pinning the runtime to Node 22 resolved it. Filing because the LTS / RSS manifestation isn't captured by #63574's current bisect and may need a backport to 24.x — happy to consolidate if maintainers consider it the same root cause.