Repository navigation
http: node-fetch throws ERR_STREAM_PREMATURE_CLOSE on keep-alive socket closures after latest security releases #63989
Description
Activity
+1 we had this happen on 22.23
Reacted by Ronnie Miller, Dom DiCicco, Yurii Averin, Christopher Winter, Bart van Halder, thomas-leeds-ravio, kk-adi, Dan Williams, Leandro, Simon Kramer and 44 morehad this happen to us too, thanks for making the issue
Reacted by Christopher Hylarides, ketanchexy, Doron Sharon, Kyle Beard, Niall Fitzpatrick, Mendel Groner, ogulcantumdogan, Wandana, Ken Harjuna Arimurti, Fahmi Tamar and 22 more+1 affected us as well. I believe this also affects v22.23.0
Reacted by Or Goldfus, Doron Sharon, Tal Kapitolnik, jordin, Kyle Beard, Niall Fitzpatrick, Chanrich Pisitjing, Rico Kõiv, Leandro, Simon Kramer and 11 moreAffected as well, really critical to fix, in all latest node versions.
Reacted by Varun Alla, Rico Kõiv, Simon Kramer, Bhavesh, Nicolai Petrov, James McNee, Nycola Plaisance, ozv-t-kiyama, Dan Oliver, Galkon and 2 more+1 22.22.3
Reacted by Chanrich Pisitjing, Rico Kõiv, Leandro, Simon Kramer, Bhavesh, Nicolai Petrov, James McNee, Maurice Kherlakian, Vishal Sanserwal, ozv-t-kiyama and 3 moreReacted by Mannyv22.23.0 is also broken!
Reacted by Charles Crossan, Chanrich Pisitjing, Rico Kõiv, Leandro, Bhavesh, James McNee, Jasper Boyd, Nycola Plaisance, ozv-t-kiyama, Nischal Suresha and 4 more- added a commit that references this issue
on Jun 18, 2026 Happened to us as well! We pinned our release versions to 24.13.0 to fix
Thanks for posting
I see the tag above, but https://github.com/edkimmel/node-24-fetch-repro is a standalone reproduction of the issue.
For our case, it needed all of the following.
- tls connection
- gzip response
- chunked response
- large enough response body to actually be chunked
- node-fetch 2.x
Reacted by Edmund ReedI observed this as well when upgrading from the Docker container
node:22.22.3tonode:22.23.0Reacted by Keisuke Yoshigai, corrog, Alberto Ayala, aayala-uniswap, Nidhi Swamy, Galkon, Numan and Lorran PegorettiReacted by BradleySame issue here, can confirm that a downgrade to 24.16 fixed the issue
Reacted by Hifzhan Akraman, Melissa Hoo, Braineffect, Jens Putzeys, Aleksey Tkachenko, ggobbe, Vlad Volkov, Shunya Abe, ozv-t-kiyama, Saranya Maity and 15 more👋 Fix/workaround info for folks who wander into this thread (figured I can save some of you some brain cycles and/or tokens):
node-fetch@2assumption here rendered no longer true by 0a22d40.Doesn't seem to impact
node-fetch@3.In case you're stuck using that lib, here's a working patch for
node-fetch@2.7.0(note there is a different call site if you're using node < 14 that I didn't bother to patch):node-fetch+2.7.0.patchdiff --git a/node_modules/node-fetch/lib/index.es.js b/node_modules/node-fetch/lib/index.es.js index aae9799..ef123d6 100644 --- a/node_modules/node-fetch/lib/index.es.js +++ b/node_modules/node-fetch/lib/index.es.js @@ -1740,7 +1740,7 @@ function fixResponseChunkedTransferBadEnding(request, errorCallback) { // if a data listener is still present we didn't end cleanly const hasDataListener = socket && socket.listenerCount('data') > 0; - if (hasDataListener && !hadError) { + if (hasDataListener && !hadError && !response.complete) { const err = new Error('Premature close'); err.code = 'ERR_STREAM_PREMATURE_CLOSE'; errorCallback(err); diff --git a/node_modules/node-fetch/lib/index.js b/node_modules/node-fetch/lib/index.js index 567ff5d..580872b 100644 --- a/node_modules/node-fetch/lib/index.js +++ b/node_modules/node-fetch/lib/index.js @@ -1744,7 +1744,7 @@ function fixResponseChunkedTransferBadEnding(request, errorCallback) { // if a data listener is still present we didn't end cleanly const hasDataListener = socket && socket.listenerCount('data') > 0; - if (hasDataListener && !hadError) { + if (hasDataListener && !hadError && !response.complete) { const err = new Error('Premature close'); err.code = 'ERR_STREAM_PREMATURE_CLOSE'; errorCallback(err); diff --git a/node_modules/node-fetch/lib/index.mjs b/node_modules/node-fetch/lib/index.mjs index 2863dd9..8a491b9 100644 --- a/node_modules/node-fetch/lib/index.mjs +++ b/node_modules/node-fetch/lib/index.mjs @@ -1738,7 +1738,7 @@ function fixResponseChunkedTransferBadEnding(request, errorCallback) { // if a data listener is still present we didn't end cleanly const hasDataListener = socket && socket.listenerCount('data') > 0; - if (hasDataListener && !hadError) { + if (hasDataListener && !hadError && !response.complete) { const err = new Error('Premature close'); err.code = 'ERR_STREAM_PREMATURE_CLOSE'; errorCallback(err);
Otherwise, you can turn off
keepAlivebut mind the potential performance impact.Reacted by Thomas ChetwinI noticed this today as well. Here is what my agent came up with as the underlying issue:
Root cause: (179ddaedfb). The fix attached a
freeSocketDataGuardlistener to the'data'event of idle sockets in thefreeSocketspool:socket.on('data', freeSocketDataGuard);
However, legacy HTTP clients like
node-fetch(specifically version 2.x) detect premature socket closures for chunked responses by checking if the socket has any active'data'listeners once the response stream closes:const hasDataListener = socket && socket.listenerCount('data') > 0; if (hasDataListener && !hadError) { const err = new Error('Premature close'); // ... }
Because of a timing race condition, the response stream's
'close'event often fires after the socket has already been returned to the agent and marked as free. Consequently,node-fetchsees the agent's guard listener (listenerCount('data') === 1) and falsely assumes the stream was prematurely closed, triggeringERR_STREAM_PREMATURE_CLOSE.Solution
Modify
lib/_http_agent.jsto guard the free sockets using the'readable'event instead of the'data'event:- Instead of subscribing to
'data'and callingsocket.resume(), subscribe to'readable'. - When any unsolicited data (or socket EOF) arrives on the idle socket, the
'readable'event will fire, and we immediately destroy the socket. - This keeps the behavior of the security guard identical while avoiding incrementing the public
'data'listener count.
Reacted by Connor Bär- Instead of subscribing to
cc @mcollina since this was your security fix commit
Same issue on Node 26.3.1 - pinning to the previous patch release 26.3.0 fixes it
Reacted by Martin Quintana64 remaining items
Given impact, do we have a date/timing for 24.18.0 (or 24.17.1) so the net writ large isn't buggy and stuck on insecure node version?
Reacted by Bluzzi, Shlomi Sasson, yaakovfeldman, Peter Halicky, Sveinung Tord Røsaker, Tobias Theobald and Erik LandvallWish I found this sooner and saved my team the all nighter!
Reacted by Erik Landvall, Pablo Chiappetti, hemantvetalBacancy, Dominik Havel, charamza, brunodevauxgojob, Vincent Giersch, JTWR, Mendel Groner and NumanOn a serious note- I would tweet/post this from the Node X account because a lot of people are going to be waking up this week from pager duty due to upstream deps broken by this.
Reacted by Vincent Giersch and Terry Mun-Andersen+1 on
node:22-alpine, first time I can say it was Node's fault not mineReacted by danekantner, Ben, Jonás Perusquía Morales, Bluzzi and Katherine SzelagWe have also experienced this when our node version management tool updated our node version from 24.16.0 to 24.17.0, which has caused DangerJS to fail since it tries to ping GitHub API for a history of commits and the response is probably large enough to trigger this.
Our solution is simply to pin node to 24.16.0 temporarily while awaiting for a proper fix to be published.
Reacted by Jonás Perusquía Moralesis this still broken?
is this still broken?
It's fixed, just not released yet.
Reacted by Souf G and AhmadHi @videvian
Is there an expected date?
Reacted by Souf G and aayala-uniswapHi @videvian
Is there an expected date?
I'm not on the team :) Just replying what I know from this thread and from https://github.com/nodejs/node/releases
But I see they released a new v22 30 minutes ago. So maybe they will have a new version for v24 soon as well.
Reacted by Miguel Zapata, Bluzzi, Reinaldo Costa and Maarten SijmHi @aduh95 / @RafaelGSS
Based on the previous comment, version 22 was released to resolve the issue. Is version 24.17 + coming as well?
Reacted by Reinaldo CostaWe're waiting for release builds to complete for 24.18.0. Hopefully (assuming the still in progress ones complete successfully) we'll be able to release in a few hours.
Reacted by Vincent Giersch, Hitallo Azevedo, Bluzzi, Joe Paolicelli, Ben Buzbee, Nicolas , Aleksei Baranov, rizaldirnm and Pierre-Yves BigourdanReacted by Miguel Zapata, Reinaldo Costa, Hitallo Azevedo, Bluzzi, Cameron Kidman, Jordan Malfara, Nicolas , rizaldirnm, RT, Lorenzo and 1 moreRe-posting from here #64098
Affected versions:
Node result
24.0.0, 24.4.0, 24.8.0 OK
24.17.0 (latest 24 LTS) ERR_STREAM_PREMATURE_CLOSE
25.9.0 OK (fixed on the 25 line)
26.3.1 (current) ERR_STREAM_PREMATURE_CLOSE (22.23.0 is affected as well
Re-posting from here #64098
Affected versions:
Node result
24.0.0, 24.4.0, 24.8.0 OK
24.17.0 (latest 24 LTS) ERR_STREAM_PREMATURE_CLOSE
25.9.0 OK (fixed on the 25 line)
26.3.1 (current) ERR_STREAM_PREMATURE_CLOSE (Reacted by Patrick Miner, Galkon and NumanCommit
Reacted by Pierre-Yves Bigourdan and Matt KadatzWe found this thread, we are running
v22.23.0and can confirm we still see the"Premature close"errrorsReacted by Aren Patel and Francesco Vezzani
Version
v24.17.0
Platform
Subsystem
http / http.Agent (keep-alive socket reuse)
What steps will reproduce the bug?
I don't yet have a reduced standalone repro, but the behavior change is sharply bisected and reproduces across two independent code paths plus a second reporter:
Both paths run through
gaxios@6.7.1→node-fetch@2.7.0→ corenode:httpwith a keep-alivehttp.Agent, making requests to*.googleapis.com:googleapis@144Drivefiles.list(background polling loop, long-lived process)@google-cloud/storage@7.14.0v4 signed-URL signing viagoogle-auth-library@9.15.0GoogleAuth.signBlob→https://iamcredentials.googleapis.com/.../:signBlobBoth began failing immediately on a deploy whose only material change was the Node runtime moving from 24.16.0 → 24.17.0 (a floating
node:24-slimbase image picked up the same-day 24.17.0 release). No dependency versions changed. Pinning the image back tonode:24.16.0-slimfully resolves both.A likely minimal-repro shape (from the related node-fetch issue below) is a keep-alive
https.Agent({ keepAlive: true, maxSockets: N }) issuing more than N sequential/concurrent requests to a chunked or gzip-encoded HTTPS endpoint. I'm happy to invest in a self-contained reproduction if that would help — wanted to file the bisect promptly given the blast radius.How often does it reproduce? Is there a required condition?
Reliably under load on 24.17.0; never on 24.16.0. Appears tied to keep-alive socket reuse — the failure is a premature close on a reused pooled socket.
What is the expected behavior? Why is that the expected behavior?
Keep-alive pooled HTTP requests that complete normally on 24.16.0 should continue to complete on 24.17.0 without the response body being reported as prematurely closed.
What do you see instead?
FetchError: Invalid response body while trying to fetch https://www.googleapis.com/drive/v3/files?...: Premature close code: ERR_STREAM_PREMATURE_CLOSE
and on the signing path:
Additional information
http.Agent" (plus the TLS/SNI changes) — anything altering keep-alive socket reuse timing is a candidate.FetchError ... Premature closeonhttps://accounts.google.com/o/oauth2/token, Node v24.17.0, Windows 11.http.Agentlevel (vs. a fix-forward in 24.17.x) would help a large slice of the Google-API-client ecosystem decide how to respond.gaxios@6→node-fetch@2, so the affected surface is broad.