ECONNRESET in Node.js: Sockets Closed Under You

ECONNRESET means the other end (or a middlebox) closed the TCP connection abruptly. In Node it's normal noise at scale unless it escapes your handler — then it's a crash.

What you'll see

Root causes

Uncaught 'error' events on sockets/requests

An 'error' event with no listener throws. http.request, net sockets, and third-party clients must have error handlers attached — .on('error') or try/catch around await.

Keep-alive idle sockets reaped by a proxy

Load balancers kill idle keep-alive connections (often ~60s). A reused dead socket triggers ECONNRESET on the next write. Solutions: shorter free-timeout than the LB's, or retry idempotent requests.

Peer crashed or rejected a big payload

Upstream OOM-kill or request-size limits can abort connections instead of responding. Correlate timestamps with upstream logs.

Fix it

  1. Add an uncaught-error safety net to see what's escaping
    process.on('uncaughtException', (e) => log.fatal({e})); process.on('unhandledRejection', (e) => log.fatal({e}));
  2. Attach error handlers to every request/socket
    const req = http.get(url, (res) => {...); req.on('error', (e) => /* classify + retry if idempotent */); req.setTimeout(5000, () => req.destroy());
  3. Tune keep-alive below the middlebox timeout
    const agent = new http.Agent({ keepAlive: true, freeTimeout: 30_000 }); // or keepAliveMsecs + maxSockets on https.Agent
  4. Retry idempotent reads once on ECONNRESET
    # wrap fetches in a retry helper: on 'ECONNRESET' && method is GET → retry with backoff; never blind-retry POSTs

Field note

undici/fetch users: set a dispatcher with keepAliveTimeout lower than your LB's idle timeout to avoid reuse-of-dead-socket resets. In clusters of pods behind a service mesh, check sidecar/log shipping overhead too — resets often correlate with proxy resource limits.

Common questions

Is ECONNRESET always a bug in my code?

No — remote closes are a fact of network life. The bug is letting the error crash your process. Handle socket/request 'error' events and decide per-call whether to retry.

Why do I get ECONNRESET only under load?

Load multiplies the odds: more keep-alive reuse (dead-socket hits), upstream queues rejecting (RST instead of 503), and LB idle reaping all scale with concurrency. Fix handlers first, then tune agents.

Ship it right the first time

Our most-documented failures, packaged as ready-to-ship starter kits: Docker, Kubernetes, and Terraform.

Browse the template store →

One-time. Yours to modify. Instant download from the NinjaOps template store.