ECONNRESET means the other side of an established TCP connection closed it abruptly with a reset (an RST packet) instead of a polite close. Node reports it as read ECONNRESET when it was waiting for data, or write ECONNRESET when it was sending. The related message Error: socket hang up (code ECONNRESET) means the peer closed the connection before sending a complete HTTP response.
Error: read ECONNRESET
at TCP.onStreamRead (node:internal/stream_base_commons:217:20) {
errno: -104,
code: 'ECONNRESET',
syscall: 'read'
}
Something ended the conversation. Your job is to work out who: the server app, a proxy or load balancer, a firewall or NAT device, or your own code.
Decision order
- Does it happen on every request or only some? Every request points to a TLS, protocol or auth mismatch. Intermittent failures point to idle connection reuse or overload.
- Does it happen on the first request of a burst after idle time? That is the classic keep-alive race.
- Does the server log a crash or restart at the same moment? Then the server app died, not the network.
Cause 1: reused keep-alive connection closed by the server
Your client holds idle connections open to reuse them. The server or load balancer closes idle connections after its own timeout. If your client sends a request at the instant the server closes the socket, you get ECONNRESET or socket hang up. Symptoms are rare, random failures on a healthy service, often after a quiet period.
Fix by making the client's idle timeout shorter than the server's. Typical server-side idle timeouts you should compare against are your reverse proxy's keepalive_timeout and your cloud load balancer's idle timeout (check the provider's documentation for the current default; they vary).
import https from 'node:https';
const agent = new https.Agent({ keepAlive: true, keepAliveMsecs: 1000, maxSockets: 50 });
If you run Node as the server behind a load balancer, do the reverse: Node's server.keepAliveTimeout (5 seconds by default) is usually shorter than a balancer's idle timeout, so the balancer reuses a connection Node already closed. Raise it above the balancer's value:
server.keepAliveTimeout = 65_000; // longer than the LB idle timeout
server.headersTimeout = 66_000; // keep this above keepAliveTimeout
Also retry idempotent requests once on ECONNRESET. Never blindly retry a POST.
Cause 2: the server process crashed or restarted
If the app dies mid-request (uncaught exception, out-of-memory kill, deploy restart), every open connection resets. Check the server log and the process manager at that timestamp. For container kills see Docker exit code 137 and Kubernetes CrashLoopBackOff. During deploys, drain connections gracefully: stop accepting new ones on SIGTERM, finish in-flight requests, then exit.
Cause 3: TLS or protocol mismatch
A server that expects TLS 1.2 or higher may reset a client offering older protocols, and some servers reset on a bad SNI name or unknown cipher. Test the endpoint:
openssl s_client -connect example.com:443 -servername example.com -tls1_2 </dev/null
curl -v https://example.com/
The TLS वर्शन चेकर shows which protocol versions the server accepts. Also confirm you are not speaking HTTPS to a plain HTTP port or the reverse; that mismatch is covered in ERR_SSL_WRONG_VERSION_NUMBER.
Cause 4: proxy, WAF or rate limiter
Web application firewalls, bot protection and rate limiters sometimes reset connections they dislike: missing User-Agent, a blocked IP, or too many requests per second. Compare a curl request with your Node request headers using the HTTP Header Checker. If a CDN fronts the origin, see whether the reset comes from the edge or the origin.
Cause 5: oversized or malformed requests
Servers reset connections for bodies above their limits or invalid framing. A wrong Content-Length, writing after calling end(), or sending a body on a request that declared none can all cause it. If the server reads a limit like nginx's client_max_body_size, that shows as 413 in most setups, but other servers simply close the socket.
Cause 6: idle NAT and firewall timeouts
Stateful firewalls and NAT gateways forget idle connections after a time. The next packet draws a reset. Long-lived database or message-queue connections through a NAT are typical. Enable TCP keep-alive probes with a short interval:
socket.setKeepAlive(true, 30_000);
Handling the error in code
An unhandled error event on a socket or request crashes the process. Always attach a listener:
req.on('error', (err) => {
if (err.code === 'ECONNRESET') return retryOnce();
throw err;
});
With fetch, a reset surfaces as TypeError: fetch failed with the real code in err.cause.code. Log err.cause, not just the message.
अगले कदम
- Read ERR_CONNECTION_REFUSED, timed out and reset explained.
- Check accepted protocol versions with the TLS वर्शन चेकर.
- See the client-side twin, ERR_CONNECTION_CLOSED.
- Monitor the endpoint with an uptime monitor.
सामान्य प्रश्न
What is the difference between ECONNRESET and EPIPE?
ECONNRESET is reported when reading, or writing, after the peer reset the connection. EPIPE means you wrote to a connection that was already closed. See Node EPIPE.
Is "socket hang up" the same thing?
It is the same underlying code. The peer closed the connection before a full HTTP response arrived.
Should I just retry?
For idempotent GET, HEAD and PUT requests, one retry with a short delay is reasonable. Fix the root cause too, since retries hide a real keep-alive mismatch.
Why does it only happen in production?
Production has load balancers, proxies and NAT devices with idle timeouts that your local machine does not.
Get notified when this fix updates
Plus occasional practical tips on uptime and monitoring, nothing else. Unsubscribe anytime.
यह पेज सामान्य जानकारी है, जिसे हमारी संपादकीय टीम ने लिखा है और दस्तावेज़ीकरण बदलने पर अपडेट किया जाता है। सर्वर और प्रदाता अलग-अलग होते हैं, इसलिए बदलाव पहले सुरक्षित तरीके से जाँचें। देखें: संपादकीय नीति और अस्वीकरण.