Connection failures: ECONNREFUSED, ECONNRESET, and friends
A connection failure means TCP itself gave up — no HTTP status, no response body, nothing from the application on the other side. The error code tells you when in the connection's life it died, and that is the single most useful diagnostic fact.
The codes, by when they fire
ECONNREFUSED | Before the connection existed. The target machine answered with a TCP RST: nothing is listening on that port. The host is reachable — the service isn't. |
EHOSTUNREACH / ENETUNREACH | Before the target machine was even reached — routing failed. Wrong network, missing route, firewall dropping silently. |
ECONNRESET | Mid-conversation. The peer (or a middlebox between you) sent RST on an established connection: process crash, forced restart, idle-timeout on a load balancer, or a proxy killing what it considers a dead connection. |
EPIPE ("broken pipe") | You wrote to a connection the peer had already closed. Same family as ECONNRESET, seen from the writing side. |
| "socket hang up" | Node's wrapper for the server closing the connection before sending a complete response — usually a crashed handler or an aggressive keep-alive timeout. |
Debugging in order
Work outward from the refused/reset distinction. For ECONNREFUSED: is the
service running, and is it listening where you think?
ss -tlnp | grep 5432 # is anything listening on the port? curl -v telnet://host:5432 # can this machine reach it?
The classic causes are the service binding 127.0.0.1 while you connect from
another host (or another container), a container port that was never published, or connecting
to localhost inside a container where "localhost" is the container itself.
For ECONNRESET mid-stream, suspect timeouts and middleboxes before code: idle
keep-alive connections killed by a load balancer (AWS ALB defaults to 60s), a proxy with a
shorter timeout than your client, or the peer process being restarted by a deploy or an
OOM kill. Check the server's logs for the same timestamp — a reset with no server-side log
line usually means a middlebox did it.
Handling it properly
Connection failures before any bytes are sent are safely retryable — with backoff and a retry budget, not a bare loop. Failures mid-request are only retryable if the request is idempotent. Pool users: retire a pooled connection on ECONNRESET/EPIPE instead of resurrecting it, and keep client keep-alive timeouts shorter than every hop in front of the server so the client, not a middlebox, decides when connections die.
Documented occurrences
11 analyzed errors across 6 libraries match this failure class. Each links to the thrown message, its source line, and documented fixes.
redis/redis-py
- Connection closed by server.
- Connection closed by server.
- Connection closed by server.
- Connection closed by server.
- Connection closed by server.