This error occurs when a TCP connection is forcibly closed by the remote server or peer before the operation completes. It typically happens during HTTP requests when the server closes the connection unexpectedly, often due to timeout mismatches, keep-alive issues, or network problems.
The ECONNRESET error is a network-level error that occurs at the TCP layer when one side of a connection sends a TCP RST (reset) packet to the other side, immediately terminating the connection. In Node.js, this manifests as "Error: read ECONNRESET" or "Error: write ECONNRESET" depending on whether you were reading from or writing to the socket when it was reset. This error is particularly common when using HTTP keep-alive connections. When a server closes an idle connection that a client is trying to reuse from its connection pool, the client receives an ECONNRESET error. The timing window between when the server decides to close the connection and when the client attempts to use it creates a race condition that's difficult to avoid entirely. The error indicates that the remote peer (server) has abruptly closed the connection without following the normal TCP connection termination handshake. This is different from a graceful close (FIN packet) and suggests either an error condition on the remote side or a timeout/resource constraint that forced the connection to be terminated immediately.
Add error handling to catch and retry ECONNRESET errors gracefully:
const http = require('http');
function makeRequest(url, retries = 3) {
return new Promise((resolve, reject) => {
http.get(url, (res) => {
let data = '';
res.on('data', (chunk) => data += chunk);
res.on('end', () => resolve(data));
}).on('error', (err) => {
if (err.code === 'ECONNRESET' && retries > 0) {
console.log(`ECONNRESET occurred, retrying... (${retries} attempts left)`);
setTimeout(() => {
makeRequest(url, retries - 1).then(resolve).catch(reject);
}, 1000); // Wait 1 second before retry
} else {
reject(err);
}
});
});
}This ensures temporary connection resets don't crash your application.
The most important fix: ensure your client timeout is shorter than the server's keep-alive timeout. If the server closes idle connections after 5 seconds, set your client timeout to 4 seconds:
const http = require('http');
const https = require('https');
// For http module
const agent = new http.Agent({
keepAlive: true,
keepAliveMsecs: 3000, // 3 seconds
timeout: 4000, // 4 seconds (shorter than server's 5s)
maxSockets: 50,
});
// For https module
const httpsAgent = new https.Agent({
keepAlive: true,
keepAliveMsecs: 3000,
timeout: 4000,
maxSockets: 50,
});
// Use the agent in requests
http.get('http://example.com', { agent }, (res) => {
// handle response
});For libraries like axios:
const axios = require('axios');
const http = require('http');
const https = require('https');
const httpAgent = new http.Agent({
keepAlive: true,
timeout: 4000,
});
const httpsAgent = new https.Agent({
keepAlive: true,
timeout: 4000,
});
const client = axios.create({
httpAgent,
httpsAgent,
timeout: 5000, // Request timeout
});Install and configure agentkeepalive with a safety buffer to close sockets before the server does:
npm install agentkeepaliveconst Agent = require('agentkeepalive');
const keepaliveAgent = new Agent({
maxSockets: 100,
maxFreeSockets: 10,
timeout: 60000, // Active socket timeout
freeSocketTimeout: 30000, // Free socket timeout (30s)
keepAliveTimeout: 60000, // Keep-alive timeout
socketActiveTTL: 110000, // Max time socket can be alive
});
const http = require('http');
http.get({
host: 'example.com',
port: 80,
path: '/',
agent: keepaliveAgent,
}, (res) => {
// handle response
});The key is setting freeSocketTimeout shorter than the server's timeout to prevent reusing dead sockets.
If you're running the Node.js server that's causing ECONNRESET errors for clients, increase the timeouts:
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World\n');
});
// Set keep-alive timeout to 65 seconds (longer than most client defaults)
server.keepAliveTimeout = 65000;
// Set headers timeout slightly higher than keepAliveTimeout
server.headersTimeout = 66000;
// Set overall request timeout
server.timeout = 120000;
server.listen(3000, () => {
console.log('Server running on port 3000');
});For Express.js:
const express = require('express');
const app = express();
const server = app.listen(3000, () => {
console.log('Server running on port 3000');
});
server.keepAliveTimeout = 65000;
server.headersTimeout = 66000;For production environments, use a sophisticated retry mechanism with exponential backoff:
async function fetchWithRetry(url, options = {}, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url, options);
return response;
} catch (error) {
const isLastAttempt = attempt === maxRetries;
const isRetriableError =
error.code === 'ECONNRESET' ||
error.code === 'ETIMEDOUT' ||
error.code === 'ECONNREFUSED';
if (isLastAttempt || !isRetriableError) {
throw error;
}
const delay = Math.min(1000 * Math.pow(2, attempt), 10000);
console.log(`Attempt ${attempt + 1} failed, retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
// Usage
fetchWithRetry('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(err => console.error('All retries failed:', err));Add detailed logging to understand when and why connections are being reset:
const http = require('http');
const agent = new http.Agent({
keepAlive: true,
});
// Monitor socket lifecycle
agent.on('free', (socket, host, port) => {
console.log(`Socket freed: ${host}:${port}`);
});
const req = http.get('http://example.com', { agent }, (res) => {
console.log(`Response status: ${res.statusCode}`);
res.on('data', () => {});
res.on('end', () => console.log('Response complete'));
});
req.on('socket', (socket) => {
console.log('Socket assigned to request');
socket.on('connect', () => console.log('Socket connected'));
socket.on('timeout', () => console.log('Socket timeout'));
socket.on('end', () => console.log('Socket ended'));
socket.on('close', (hadError) => console.log(`Socket closed (error: ${hadError})`));
});
req.on('error', (err) => {
console.error('Request error:', {
code: err.code,
message: err.message,
syscall: err.syscall,
});
});Keep-Alive Timeout Race Condition:
The ECONNRESET error is often unavoidable due to a fundamental race condition in HTTP keep-alive. When a server decides to close an idle connection, it sends a FIN packet. If a client sends a request on that socket in the brief moment after the server has decided to close but before the client receives the FIN, the server responds with a RST (reset), causing ECONNRESET. The solution is not to eliminate the error entirely but to handle it gracefully with retries.
Node.js Default Behavior:
Node.js HTTP server defaults changed in version 8.0.0. The default server.keepAliveTimeout is 5 seconds, while many client libraries use longer defaults (15+ seconds). This mismatch is the root cause of many ECONNRESET errors in production.
Connection Pool Considerations:
When using database drivers (PostgreSQL, MySQL, MongoDB) or HTTP client libraries with connection pooling, configure pool-specific timeout settings. Most pools have idle timeout settings that should be shorter than the server's connection timeout.
Load Balancer and Reverse Proxy Impact:
Services like AWS ALB, NGINX, and CloudFront have their own timeout settings that can cause ECONNRESET. For example, AWS ALB has a 60-second idle timeout by default. Ensure your client timeouts align with all intermediary infrastructure.
Debugging Production Issues:
Use tools like tcpdump or Wireshark to capture TCP RST packets and determine which side is initiating the reset. The NODE_DEBUG=http environment variable enables verbose HTTP module debugging in Node.js.
VPN and Corporate Firewall Issues:
Corporate networks often have aggressive firewall rules that reset idle TCP connections after 30-60 seconds. If errors only occur in specific network environments, investigate firewall and NAT timeout settings.
Error: Listener already called (once event already fired)
EventEmitter listener already called with once()
Error: EACCES: permission denied, open '/root/file.txt'
EACCES: permission denied
Error: Invalid encoding specified (stream encoding not supported)
How to fix Invalid encoding error in Node.js readable streams
Error: EINVAL: invalid argument, open
EINVAL: invalid argument, open
TypeError: readableLength must be a positive integer (stream config)
TypeError: readableLength must be a positive integer in Node.js streams