Node.js Security Fix Silently Broke node-fetch, which broke other tools
Observations
SubscribeSign in
Node.js Security Fix Silently Broke node-fetch, which broke other tools
JD<br>Jun 19, 2026
Share
On June 18, 2026, Node.js released updates for its active release lines, including Node 22.23.0 (LTS) and Node 24.17.0 . These releases contained security patches, notably a fix for a Response Queue Poisoning vulnerability in http.Agent.<br>After that, our development pipeline began failing. We detected it first thanks to our End-To-End tests. The error looked like this:<br>FetchError: Invalid response body while trying to fetch https://...: Premature close<br>code: 'FetchError',<br>type: 'system',<br>errno: 'ERR_STREAM_PREMATURE_CLOSE'
It first looked like a transient network issue, so we debugged it by starting various connectivity checks. Turns out, a security patch in the Node.js core triggered a latent false-positive guard in a widely used, legacy HTTP wrapper.<br>Here is a short “What happend”, since it starts to gain traction in the community.
Root Cause Chain
1. Node.js Socket Retention
To patch a security vulnerability related to request smuggling and response queue poisoning, Node.js core modified the behavior of http.Agent (specifically this commit).<br>Historically, when an HTTP response completed, certain internal socket data listeners were detached. The security fix altered this lifecycle: a socket data listener now remains attached after the response completes to ensure the socket state is cleanly managed and tracked. From a pure HTTP networking perspective, this behavior is valid.<br>2. Downstream Trigger: node-fetch@2 False Positive
node-fetch@2 (and libraries wrapping it, like cross-fetch@4 or Google’s gaxios) contains a safety check designed to catch incomplete streams.<br>The library inspects the underlying socket to see if any data listeners are still attached after the response stream signals completion.<br>Before Node 22.23.0: No listeners remained → node-fetch assumed the stream completed successfully.
After Node 22.23.0: The socket listener is still present → node-fetch mistakenly interprets this as an aborted or interrupted stream, aborts the operation, and throws ERR_STREAM_PREMATURE_CLOSE.
3. How the bug is triggered
Currently it looks like it is not triggered on every HTTP call, but when you meet this conditions:<br>HTTPS/TLS Connection
Chunked Transfer Encoding
Compression enabled
HTTP Keep-Alive keeping the socket open for reuse
If this happens, the problem should occur.<br>This bug breaks, even if you haven’t change a single line of code
A pipeline that passed cleanly on June 17 was broken on June 19.<br># DOCKERFILE<br>FROM node:22-alpine
Because you often rely on floating tags (like node:24-alpine or lts/jod in .nvmrc), automatic CI/CD environments pull a fresh NodeJs image, containing the security fix but triggering the bug.<br>Impact
NodeJs Issue
See this GitHub Issue for more details in the NodeJs repository.<br>Firebase CLI (firebase-tools)
See this GitHub Issue for more details in the Firebase-Tools repository.<br>About node-fetch@2
node-fetch@2 is effectively unmaintained and will not receive a patch for this modern socket behavior. If your application relies on older SDKs that bundle it, the cleanest path forward is to bypass the polyfill layer entirely and adopt Node's native fetch global (built on undici), which does not rely on the legacy http.Agent infrastructure
Share
Discussion about this post<br>CommentsRestacks
TopLatestDiscussions
No posts
Ready for more?
Subscribe
© 2026 Jan-David Stärk · Privacy ∙ Terms ∙ Collection notice<br>Start your SubstackGet the app<br>Substack is the home for great culture
This site requires JavaScript to run correctly. Please turn on JavaScript or unblock scripts