Deploy Error Decoder

ECONNRESET & 'socket hang up' — why connections reset and how to fix it

Quick answer: ECONNRESET ("read ECONNRESET" / "socket hang up") means the remote side closed the TCP connection abruptly while your app was using it. It's usually a reused keep-alive socket the server already dropped, an upstream that restarted mid-request, or a proxy idle-timeout. Make the client resilient: retry idempotent requests, and keep your client's idle timeout shorter than the server's.

What the error looks like

It surfaces on the reading side of a connection that vanished:

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

# via an HTTP client it often reads as:
Error: socket hang up   (code: 'ECONNRESET')

"Reset" is different from "refused": the connection was established, then the other end tore it down. That points at a connection that went stale or an upstream that died, not a wrong address.

Why it happens

A reused keep-alive socket went stale

Your client reused a pooled connection the server had already closed, so the first write resets.

The upstream restarted mid-request

A deploy, scale-down, or crash on the other side severed connections that were in flight.

A proxy idle-timeout fired

A load balancer or proxy closed an idle connection your client still thought was open.

The server dropped you on purpose

A rate limiter, max-body guard, or WAF reset the connection instead of replying.

Diagnose it in three steps

1

Find which host resets

# Log the request target on error so you know who hung up
# Is it always the same upstream? That narrows it fast.
2

First request or only reused?

# If it only fails on the 2nd+ request to a host, it's stale keep-alive.
# If it fails right when you deploy the upstream, it's the restart.
3

Compare the timeouts

# Server idle timeout must be >= client keep-alive timeout,
# or the client will keep grabbing sockets the server just closed.
The real fix

Make the client expect dead sockets

You can't stop a peer from ever closing a connection — so handle it. Keep idle sockets short-lived and retry safe requests:

const http = require('http');
const agent = new http.Agent({
  keepAlive: true,
  // give up on an idle socket BEFORE the upstream does
  timeout: 30_000,
});

// Retry only idempotent requests (GET/PUT/DELETE), with backoff.
// Never blindly retry a POST that may have already been processed.
How Infraveil handles this

Deploys that drain instead of reset

A large share of production ECONNRESETs come from the upstream restarting mid-request on deploy. Infraveil is a backend operations control plane that runs on your own servers: its agent drains connections on the old instance before stopping it, and only shifts traffic to a health-checked new one — so a rollout doesn't sever live sockets. Every deploy is approval-gated and recorded, with one-click rollback.

Connections drain on the old instance before it's stopped — fewer reset-on-deploy errors
Health-gated cutover means traffic only moves to a ready instance
Every deploy recorded with one-click rollback, on servers you control

Frequently asked questions

What does ECONNRESET mean?

The remote end abruptly closed an established TCP connection (a "connection reset by peer"). Your side was mid-read or mid-write when the socket disappeared.

Is ECONNRESET my fault or the server's?

Often neither is "broken" — it's a stale pooled connection or an upstream that restarted. The durable fix lives on the client: short idle timeouts plus retries for idempotent requests.

How do I fix 'socket hang up' in axios?

Use a keep-alive agent with a timeout shorter than the upstream's, and add retry-with-backoff (e.g. axios-retry) for safe methods. Investigate the upstream if it correlates with its deploys.

ECONNRESET vs ECONNREFUSED?

Refused means nothing accepted the connection (service down / wrong port). Reset means a connection was established and then torn down by the other side.