# Node.js: “Error: read ECONNRESET” / “socket hang up”

> Error: read ECONNRESET or socket hang up in Node.js. Find which side closed the connection, match keep-alive timeouts, retry safe requests, handle errors.

Canonical: https://app369.com/fix/node-econnreset/
Last updated: 2026-11-08

A request from your Node.js app fails with Error: read ECONNRESET, or Error: socket hang up with code ECONNRESET, sometimes only now and then. The other side of the connection (an API, a database, a proxy, or your own server behind a load balancer) closed it while your code was still using it.

**Short answer**

Work out which connection was reset: an outgoing request to an API or database, or requests into your server. For outgoing calls, retry idempotent requests (GET, and anything with an idempotency key) with a short backoff, and add a timeout. For your own server behind a load balancer or proxy, set server.keepAliveTimeout longer than the proxy’s idle timeout, so Node.js never closes a connection the proxy is about to reuse. Always handle the error event on sockets and streams, or one reset crashes the process.

## **Why the connection was reset, and the fix**

1. 01 ### **The other side closed an idle keep-alive connection**
  > “A connection was forcibly closed by a peer. This normally results from a loss of the connection on the remote socket due to a timeout or reboot.” [**Node.js, Errors: common system errors (ECONNRESET)**](https://nodejs.org/api/errors.html) HTTP clients reuse connections. If the server or a proxy drops an idle one at the same moment your app sends a new request on it, the request fails with ECONNRESET. This shows up as rare, random failures; Node.js’s built-in fetch may report it as UND_ERR_SOCKET (other side closed) instead. Retry requests that are safe to repeat, and keep client idle timeouts shorter than the server’s.```
async function getWithRetry(url, tries = 3) {
  for (let i = 1; ; i++) {
    try {
      return await fetch(url, { signal: AbortSignal.timeout(10_000) });
    } catch (err) {
      const code = err.cause?.code ?? err.code;
      if (i >= tries || !['ECONNRESET', 'UND_ERR_SOCKET'].includes(code)) throw err;
      await new Promise((r) => setTimeout(r, 200 * i));
    }
  }
}
```
2. 02 ### **Your server closes connections before the load balancer does**
  > “The number of milliseconds of inactivity a server needs to wait for additional incoming data, after it has finished writing the last response, before a socket will be destroyed.” [**Node.js, HTTP: server.keepAliveTimeout**](https://nodejs.org/api/http.html) Behind AWS ALB, nginx or another proxy, Node.js’s default keep-alive timeout can be shorter than the proxy’s idle timeout. Node.js closes the socket, the proxy sends the next request on it, and the client gets a 502 or a reset. Make the Node.js timeout longer than the proxy’s, and headersTimeout a little longer still.```
const server = app.listen(3000);
// proxy idle timeout is 60 s (the ALB default)
server.keepAliveTimeout = 65_000;
server.headersTimeout = 66_000;
```
3. 03 ### **The server crashed, restarted or rejected the request** A reset also happens when the remote process crashes mid-request, a container restarts during a deploy, a request body is larger than the server accepts, or a firewall cuts long connections. Check the other side’s logs at the same timestamp before changing your own code.
4. 04 ### **An unhandled error event crashes the process** A reset on a socket or stream emits an error event. With no listener, Node.js throws it and the whole process exits. Handle it where the stream is created, and log it with the request it belongs to.```
const req = https.request(options, onResponse);
req.on('error', (err) => {
  console.error('upstream failed', err.code);
});
req.end();
```

Checked against the vendor’s docs on November 8, 2026: [Node.js: Errors (common system errors)](https://nodejs.org/api/errors.html); [Node.js: HTTP (server.keepAliveTimeout, server.headersTimeout)](https://nodejs.org/api/http.html).

## **Rather hand it over?**

Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.

- [from $450**App Fix Ticket**One reproducible bug in an existing mobile or web app, diagnosed and fixed.**See scope and start**](https://app369.com/fixed-price/flutter-firebase-bug-fix/)
- [from $450**AI-Built App Rescue**A Lovable, Bolt or Cursor app checked end to end in 3 days, then its worst problems fixed at a fixed price.****No issues worth fixing, no charge.**** If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.**See scope and start**](https://app369.com/fixed-price/ai-app-rescue/)

Related: [**ECONNREFUSED: connection refused**](https://app369.com/fix/econnrefused-connection-refused/) · [**Node.js EADDRINUSE: address already in use**](https://app369.com/fix/node-eaddrinuse-address-already-in-use/) · [**“Uncaught (in promise)” errors**](https://app369.com/fix/uncaught-in-promise/) · [**“self-signed certificate in certificate chain”**](https://app369.com/fix/node-self-signed-certificate-in-chain/)

Questions

## **More on this error**

<details>

<summary>**What is the difference between ECONNRESET and ECONNREFUSED? **+</summary>

ECONNREFUSED means nothing accepted the connection at all, so the server is down or the address is wrong. ECONNRESET means a connection was open and the other side closed it abruptly.

</details>

<details>

<summary>**Is it safe to retry every request on ECONNRESET? **+</summary>

Only requests that can run twice without harm: reads, and writes with an idempotency key. A POST that charges a card or sends an email may already have run, so check before retrying it.

</details>

<details>

<summary>**Can you fix it for me? **+</summary>

If one bug is the whole problem, the App Fix Ticket covers it: a written diagnosis first, then the fix. If the app has more going on, the Launch Check is a written check of the whole app. Each issue found is rated critical, high, medium or low, with where it is and how it was confirmed. No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.

</details>

A different error?

## **Describe it and get a written quote**

Send the error and what you were doing. You get a written scope, a fixed price and a timeline; Simon replies by email, usually within one business day.

[**Describe your problem **→](https://app369.com/contact/) [**All fixes**](https://app369.com/fix/)