Error: read ECONNRESET / socket hang up

⚡ Fastest fix

The peer closed the connection. If you're the client, retry idempotent requests once with backoff and use a keep-alive agent that recycles dead sockets. If you're the server, it's usually a client that disconnected — handle the socket error so it doesn't crash you.

// client: retry idempotent calls on a transient reset
for (let i = 0; i < 3; i++) {
  try { return await fetch(url); }
  catch (e) { if (e.cause?.code !== 'ECONNRESET' || i === 2) throw e; }
}

What you're seeing

Error: read ECONNRESET
    at TCP.onStreamRead (node:internal/stream_base_commons:217:20)
  errno: -104, code: 'ECONNRESET', syscall: 'read'

// or, when no response ever arrived:
Error: socket hang up
    at connResetException (node:internal/errors:720:14)
  code: 'ECONNRESET'

The behaviour is essentially the same across Node.js 18, 20, and 22. Modern Node powers fetch() with Undici, so the examples below use the built-in fetch — but the causes and fixes apply equally to axios, node:http, and database drivers.

30-second triage

Fix 1 — Retry idempotent requests (most common)

When: occasional resets on GET/PUT/DELETE calls to an API or database driver.

async function getWithRetry(url, tries = 3) {
  for (let i = 0; i < tries; i++) {
    try {
      return await fetch(url);
    } catch (e) {
      const code = e.cause?.code || e.code;
      if (code !== 'ECONNRESET' || i === tries - 1) throw e;
      await new Promise(r => setTimeout(r, 200 * 2 ** i)); // backoff
    }
  }
}

Most resets are one-off (a recycled load-balancer socket, a brief network blip). A single retry with exponential backoff clears the majority. Only retry requests that are safe to repeat — see the POST note below.

Same pattern with axios — check err.code the same way:

import axios from 'axios';

async function getWithRetry(url, tries = 3) {
  for (let i = 0; i < tries; i++) {
    try {
      return await axios.get(url);
    } catch (err) {
      if (err.code !== 'ECONNRESET' || i === tries - 1) throw err;
      await new Promise(r => setTimeout(r, 200 * 2 ** i));
    }
  }
}
// or offload it entirely with the axios-retry plugin

Fix 2 — Reuse sockets safely with a keep-alive agent

When: high request volume; the first request after an idle gap resets.

import { Agent } from 'undici';

// recycle idle sockets before the server drops them
const agent = new Agent({
  keepAliveTimeout: 10_000,      // close our idle sockets at 10s
  keepAliveMaxTimeout: 30_000,
});

await fetch(url, { dispatcher: agent });

The classic intermittent ECONNRESET is a stale pooled socket: your agent hands you a connection the server closed seconds ago. Setting your keep-alive timeout below the server's idle timeout means you never reuse a dead one.

Fix 3 — Rule out a proxy, VPN, or TLS inspector

When: it only happens on a work network, a VPN, or against one specific host.

# reproduce outside Node — if curl also resets, it's the network path
curl -v https://api.example.com/health

# honor the corporate proxy in Node
export HTTPS_PROXY=http://proxy.corp:8080
node app.js

TLS-inspecting firewalls reset handshakes they can't decrypt, and proxies reset hosts on a blocklist. If curl -v outside Node fails the same way, the fix is network/proxy configuration, not your code.

Fix 4 — Stop client disconnects from crashing the server

When: your own HTTP server logs ECONNRESET and sometimes the process dies.

server.on('request', (req, res) => {
  req.on('error', (err) => {
    if (err.code === 'ECONNRESET') return;   // client went away — safe to ignore
    console.error(err);
  });
});

In Express, catch it in an error-handling middleware (four args) so a disconnect doesn't bubble up:

// error-handling middleware — must be registered last, with 4 args
app.use((err, req, res, next) => {
  if (err.code === 'ECONNRESET') return;   // client disconnected — ignore
  next(err);                               // let real errors flow on
});

A browser tab closed mid-request or a cancelled fetch resets the socket. That's normal traffic, not a bug — you just need a per-socket/per-request error handler so the unhandled event doesn't crash Node.

⚠ A word on process.on('uncaughtException'): you'll see advice to swallow ECONNRESET there as a global net. Node's own docs warn that after an uncaughtException the process may be in an undefined state and you should log and restart, not resume. The correct fix is the per-request/per-socket error handler above; a global filter is a pragmatic last resort, and if you find yourself relying on it, something upstream is still unhandled.

Fix 5 — Raise timeouts for slow or large transfers

When: big uploads/downloads or slow upstreams reset partway through.

// server: allow slow clients more headroom
server.requestTimeout = 60_000;
server.headersTimeout = 65_000;
server.keepAliveTimeout = 61_000;   // keep > upstream/LB idle timeout

POST note: never blindly retry a non-idempotent POST — the server may have already processed it. Add an idempotency key so a retry is safe, then apply Fix 1.

Why this happens

TCP has a "forcible close": one side sends an RST packet and the connection dies immediately, discarding anything in flight. When your side next tries to read, the OS returns ECONNRESET ("connection reset by peer"). Node surfaces the raw read failure as read ECONNRESET; when the socket closes before a full HTTP response is parsed, Node instead reports socket hang up. Either way the trigger is on the other end — a server crash, an idle-timeout on a proxy/load-balancer, a pool recycling a connection, or a firewall dropping the flow. That's why the durable fixes are resilience (retry, keep-alive tuning) rather than a one-line code change.

Where it comes from

SituationWho reset itGo to
Intermittent on API callsrecycled LB / pooled socketFix 1 + Fix 2
First call after idleserver closed a keep-alive socketFix 2
Only on VPN / work networkproxy or TLS inspectorFix 3
Your server logs itclient disconnected mid-requestFix 4
Large upload/slow endpointrequest/idle timeoutFix 5

✓ Confirm it's fixed

  • Loop the request 100× (or leave it idle 60s, then fire) — no reset should surface.
  • On the server, disconnect a client mid-request (close the tab / curl then Ctrl-C) — the process must stay up.
  • Watch logs under load for a day; a healthy service shows near-zero client-side ECONNRESET.

Frequently Asked Questions

What does ECONNRESET mean in Node.js?

ECONNRESET means the other end of the TCP connection closed it abruptly (sent a RST packet) while your side still expected data. read ECONNRESET is your side failing to read from a connection the peer already dropped; socket hang up is the socket closing before a full response arrived. It is a transport-level event, not an application error.

What is the difference between ECONNRESET and socket hang up?

Both come from the connection dying early. ECONNRESET is the OS telling Node the peer reset the socket. socket hang up is Node's own message when a socket closes before the expected response headers/body were received — for example a server that accepted the request but crashed or timed out before replying.

Why do I get ECONNRESET intermittently on outbound requests?

The most common cause is a stale keep-alive socket: your HTTP agent reuses a pooled connection the server has already closed. Enable a keep-alive agent with a sane idle timeout, or retry idempotent requests once on ECONNRESET. Load balancers and proxies that idle out connections cause the same pattern.

Should I just retry on ECONNRESET?

For idempotent requests (GET, PUT, DELETE) a single retry with backoff is the correct, standard fix, because most resets are transient. Don't blindly retry non-idempotent POSTs that may already have been processed server-side; make them idempotent with an idempotency key first, then retry.

Why does ECONNRESET crash my Node server?

When a client disconnects mid-request, the socket emits an error event. An unhandled error on a stream/socket throws and can crash the process. Attach an error handler (req.on('error', ...) / socket.on('error', ...)); a client-side ECONNRESET is usually safe to log and ignore, not a bug in your code.

Can a proxy or VPN cause ECONNRESET?

Yes. Corporate proxies, VPNs, and TLS-inspecting firewalls frequently reset connections they don't like — wrong CA, blocked host, or an idle timeout. Test the same request with curl -v outside Node; if curl also resets, it's the network path, not your code.

More Node.js & network errors

Browse the full reference for Node.js, network, and build errors — exact message, cause, and fix.

All Error References net::ERR_CONNECTION_REFUSED HTTP Status Codes
About the author

Pasindu Ishan is a software developer based in Sri Lanka. He builds privacy-first developer tools at JSON Dev Tools.