Skip to content

http: node-fetch throws ERR_STREAM_PREMATURE_CLOSE on keep-alive socket closures after latest security releases #63989

Description

@tobalsgithub

Version

v24.17.0

Platform

Reproduced on Linux x64 (Cloud Run / gVisor, Debian 12). A second, independent
  report is on Windows 11 x64 (see firebase-tools #10681 below), so this appears
  to be platform-independent and runtime-version-driven.

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 → core node:http with a keep-alive http.Agent, making requests to *.googleapis.com:

  1. googleapis@144 Drive files.list (background polling loop, long-lived process)
  2. @google-cloud/storage@7.14.0 v4 signed-URL signing via google-auth-library@9.15.0
    GoogleAuth.signBlob → https://iamcredentials.googleapis.com/.../:signBlob

Both 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-slim base image picked up the same-day 24.17.0 release). No dependency versions changed. Pinning the image back to node:24.16.0-slim fully 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:

  SigningError: Invalid response body while trying to fetch
  https://iamcredentials.googleapis.com/.../:signBlob: Premature close
    gaxios@6.7.1/gaxios.ts:157
    google-auth-library@9.15.0 GoogleAuth.signBlob

Additional information

  • Suspected cause: the 24.17.0 changelog entry "http: fix response queue poisoning in http.Agent" (plus the TLS/SNI changes) — anything altering keep-alive socket reuse timing is a candidate.
  • Independent same-day report (different OS, different Google tool): Firebase login issue firebase/firebase-tools#10681 — FetchError ... Premature close on https://accounts.google.com/o/oauth2/token, Node v24.17.0, Windows 11.
  • Latent library-side bug being exposed: Malformed chunked response detector creates false-positive ‘premature close’ errors when HTTP Agent involved node-fetch/node-fetch#1767 ("Malformed chunked response detector creates false-positive 'premature close' errors when HTTP Agent involved"). It's plausible 24.17.0 is correctly surfacing a long-standing node-fetch defect rather than itself being wrong — but since the observable trigger is the runtime bump and node-fetch@2 is effectively unmaintained, confirmation of whether this is intended/expected behavior at the http.Agent level (vs. a fix-forward in 24.17.x) would help a large slice of the Google-API-client ecosystem decide how to respond.
  • The whole googleapis / @Google-Cloud / google-auth-library stack still depends transitively on gaxios@6 → node-fetch@2, so the affected surface is broad.

Activity

  1. tomkosm commented on Jun 18, 2026

    @tomkosm

    +1 we had this happen on 22.23

  2. RaminKav commented on Jun 18, 2026

    @RaminKav

    had this happen to us too, thanks for making the issue

  3. derekmeegan commented on Jun 18, 2026

    @derekmeegan

    +1 affected us as well. I believe this also affects v22.23.0

  4. dorsha commented on Jun 18, 2026

    @dorsha

    Affected as well, really critical to fix, in all latest node versions.

  5. ami-descope commented on Jun 18, 2026

    @ami-descope

    +1 22.22.3

  6. newman-dani commented on Jun 18, 2026

    @newman-dani

    v22.23.0 is also broken!

  7. mmgroner commented on Jun 18, 2026

    @mmgroner

    Happened to us as well! We pinned our release versions to 24.13.0 to fix

    Thanks for posting

  8. edkimmel commented on Jun 18, 2026

    @edkimmel

    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
  9. crossan007 commented on Jun 18, 2026

    @crossan007

    I observed this as well when upgrading from the Docker container node:22.22.3 to node:22.23.0

    nodejs/docker-node@10365a8

  10. ogulcantumdogan commented on Jun 18, 2026

    @ogulcantumdogan

    Same issue here, can confirm that a downgrade to 24.16 fixed the issue

  11. jasisk commented on Jun 18, 2026

    @jasisk

    👋 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@2 assumption 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.patch
    diff --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 keepAlive but mind the potential performance impact.

  12. codyoss commented on Jun 18, 2026

    @codyoss

    I noticed this today as well. Here is what my agent came up with as the underlying issue:

    Root cause: (179ddaedfb). The fix attached a freeSocketDataGuard listener to the 'data' event of idle sockets in the freeSockets pool:

    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-fetch sees the agent's guard listener (listenerCount('data') === 1) and falsely assumes the stream was prematurely closed, triggering ERR_STREAM_PREMATURE_CLOSE.

    Solution

    Modify lib/_http_agent.js to guard the free sockets using the 'readable' event instead of the 'data' event:

    • Instead of subscribing to 'data' and calling socket.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.
  13. ljharb commented on Jun 18, 2026

    @ljharb
    SponsorMember

    cc @mcollina since this was your security fix commit

  14. mdatz commented on Jun 19, 2026

    @mdatz

    Same issue on Node 26.3.1 - pinning to the previous patch release 26.3.0 fixes it

  15. 64 remaining items

  16. broksonic21 commented on Jun 22, 2026

    @broksonic21

    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?

  17. nicolasiscoding commented on Jun 23, 2026

    @nicolasiscoding

    Wish I found this sooner and saved my team the all nighter!

  18. nicolasiscoding commented on Jun 23, 2026

    @nicolasiscoding

    On 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.

  19. CoopTRUE commented on Jun 23, 2026

    @CoopTRUE

    +1 on node:22-alpine, first time I can say it was Node's fault not mine

  20. terrymun commented on Jun 23, 2026

    @terrymun

    We 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.

  21. jonaspm commented on Jun 23, 2026

    @jonaspm

    is this still broken?

  22. videvian commented on Jun 23, 2026

    @videvian

    is this still broken?

    It's fixed, just not released yet.

  23. miguelzapataj commented on Jun 23, 2026

    @miguelzapataj

    Hi @videvian

    Is there an expected date?

  24. videvian commented on Jun 23, 2026

    @videvian

    Hi @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.

  25. miguelzapataj commented on Jun 23, 2026

    @miguelzapataj

    Hi @aduh95 / @RafaelGSS

    Based on the previous comment, version 22 was released to resolve the issue. Is version 24.17 + coming as well?

  26. richardlau commented on Jun 23, 2026

    @richardlau
    Member

    We'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.

  27. instilled commented on Jun 23, 2026

    @instilled

    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 (

  28. albertux commented on Jun 23, 2026

    @albertux

    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 (

  29. Lucifer-AI-666 commented on Jun 28, 2026

    @Lucifer-AI-666

    Commit

  30. aren55555 commented on Jul 7, 2026

    @aren55555

    👀 @mdmarshmallow

    We found this thread, we are running v22.23.0 and can confirm we still see the "Premature close" errrors

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

Metadata

Metadata

Assignees

Labels

confirmed-bugIssues and PRs for confirmed bugs.httpIssues and PRs related to the http subsystem.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions