▲31 ▼2 @venv.wraith 2026-09-04 nodejs fetch networking

Node fetch ECONNRESET every few thousand requests: the keep-alive socket the server already closed

verbatim errorTypeError: fetch failed at Object.fetch (node:internal/deps/undici/undici:11576:11) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) cause: Error: read ECONNRESET at TCP.onStreamRead (node:internal/stream_base_commons:217:27)

Problem

A Node 20 worker calling one upstream API a few hundred thousand times a day: every few thousand requests, fetch dies with

TypeError: fetch failed at Object.fetch (node:internal/deps/undici/undici:11576:11) cause: Error: read ECONNRESET at TCP.onStreamRead (node:internal/stream_base_commons:217:27)

It is load-correlated — more traffic, more resets — and retrying by hand always succeeds, which smells like a bug in our retry logic. It was not.

Root cause

Node's global fetch (undici) pools connections with keep-alive. The upstream (or an idle-timeout load balancer in front of it) closes idle sockets after N seconds, but undici's pool considers a socket usable until the close arrives. Your next request is written onto a socket the server is tearing down: ECONNRESET. The race widens exactly when traffic spikes, because a busy event loop delays processing the FIN that would have marked the socket dead.

Node has known about this class since forever — the classic fix for http.Agent was keepAliveMsecs and retry-on-idle-socket. Undici exposes the same escape hatch.

fix preview — first 3 of 14 lines (ts), truncated:
import { Agent, RetryAgent, setGlobalDispatcher } from 'undici'; const upstream = new Agent({ … 11 more lines in the fix

🔒 the fix — including 2 code blocks — is members-only. $1/mo unlocks everything.

✅ 1 confirmation · Node 20.12.1

🔒 comments and voting are for members. $1/mo · every diagnosis is free to read, plus 3 complete sample fixes.