A "socket hang up" error occurs when a network connection is unexpectedly closed or terminated before the operation completes. This typically happens during HTTP requests when a server closes the connection without properly finishing the response, or when the socket times out waiting for data.
The "socket hang up" error indicates that the underlying TCP socket connection was closed abruptly or the other end of the connection stopped responding before the operation finished. Unlike ECONNRESET (which is a forceful reset), a socket hang up can occur when the connection simply ends without any data being exchanged for a period of time, or when a server closes the socket prematurely. In Node.js, this manifests as "Error: socket hang up" and typically includes the error code ECONNRESET. The error occurs when the client is waiting for a response from the server, but the server closes or the connection times out before the complete response is received. This is a common issue with HTTP keep-alive connections, long-running requests, and when dealing with slow networks or unresponsive servers. The socket hang up is a network-level error that signals either a timeout, an incomplete response, or a connection unexpectedly terminated by the remote server. This is distinct from properly closed connections (which emit an 'end' event) and is always treated as an error condition.
Implement error handling on both client and server to gracefully handle socket hang up errors:
const http = require('http');
function makeRequest(url, timeout = 30000) {
return new Promise((resolve, reject) => {
const req = http.get(url, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
resolve(data);
});
});
// Handle socket hang up errors
req.on('error', (err) => {
if (err.code === 'ECONNRESET' || err.message === 'socket hang up') {
console.error('Socket hang up detected, retrying...');
reject(err);
} else {
reject(err);
}
});
// Set a timeout for the request
req.setTimeout(timeout, () => {
req.destroy();
reject(new Error('Request timeout - socket hung up'));
});
});
}
// Usage with retry logic
async function fetchWithRetry(url, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await makeRequest(url);
} catch (err) {
if (i === maxRetries - 1) throw err;
console.log(`Attempt ${i + 1} failed, retrying...`);
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
}A common cause of socket hang up is not properly terminating requests and responses. Always call end() to complete the operation:
// Client: Always call req.end() to close the request
const http = require('http');
const postData = JSON.stringify({ name: 'John' });
const options = {
hostname: 'example.com',
port: 80,
path: '/api/user',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(postData)
}
};
const req = http.request(options, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log('Response received:', data);
});
});
req.on('error', (err) => {
console.error('Request error:', err.message);
});
// CRITICAL: Always end the request
req.write(postData);
req.end();// Server: Always call res.end() to close the response
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('Processing your request...\n');
// Simulate some processing
setTimeout(() => {
res.write('Done!\n');
// CRITICAL: Always end the response
res.end();
}, 1000);
});
server.listen(3000);Configure socket timeouts and keep-alive options to prevent premature disconnections:
const http = require('http');
const https = require('https');
// Create an agent with proper timeout settings
const httpAgent = new http.Agent({
keepAlive: true,
keepAliveMsecs: 30000, // Keep alive every 30 seconds
timeout: 60000, // 60 second socket timeout
maxSockets: 50,
maxFreeSockets: 10,
freeSocketTimeout: 30000, // Free socket timeout
});
const httpsAgent = new https.Agent({
keepAlive: true,
keepAliveMsecs: 30000,
timeout: 60000,
maxSockets: 50,
maxFreeSockets: 10,
freeSocketTimeout: 30000,
});
// Use agents in requests
const options = {
hostname: 'example.com',
path: '/api/data',
agent: httpAgent,
};
http.get(options, (res) => {
// Handle response
res.on('data', () => {});
res.on('end', () => console.log('Complete'));
});For axios:
const axios = require('axios');
const http = require('http');
const https = require('https');
const client = axios.create({
httpAgent: new http.Agent({
keepAlive: true,
timeout: 60000,
}),
httpsAgent: new https.Agent({
keepAlive: true,
timeout: 60000,
}),
timeout: 30000, // Request timeout
});
client.get('https://api.example.com/data')
.then(res => console.log(res.data))
.catch(err => console.error(err.message));A common cause of socket hang up is using the wrong protocol. Ensure you use https module for HTTPS URLs and http for HTTP:
const http = require('http');
const https = require('https');
const url = require('url');
function fetchUrl(urlString) {
return new Promise((resolve, reject) => {
const parsed = url.parse(urlString);
// Use https for HTTPS URLs, http for HTTP URLs
const protocol = parsed.protocol === 'https:' ? https : http;
const req = protocol.get(parsed, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
resolve(data);
});
});
req.on('error', (err) => {
reject(err);
});
req.end();
});
}
// This will work correctly for both HTTP and HTTPS URLs
fetchUrl('https://example.com/api')
.then(data => console.log(data))
.catch(err => console.error('Socket hang up:', err.message));Socket hang ups are often transient, so implementing retry logic is critical for reliability:
async function fetchWithExponentialBackoff(url, options = {}, maxRetries = 3) {
let lastError;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 30000);
const response = await fetch(url, {
...options,
signal: controller.signal,
});
clearTimeout(timeout);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response;
} catch (err) {
lastError = err;
const isRetriable =
err.message === 'socket hang up' ||
err.code === 'ECONNRESET' ||
err.code === 'ETIMEDOUT' ||
err.code === 'ECONNREFUSED' ||
err.name === 'AbortError';
if (!isRetriable || attempt === maxRetries) {
throw err;
}
// Exponential backoff: 1s, 2s, 4s, etc.
const delay = Math.min(1000 * Math.pow(2, attempt), 10000);
console.log(`Attempt ${attempt + 1} failed (${err.message}), retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError;
}
// Usage
fetchWithExponentialBackoff('https://api.example.com/data')
.then(res => res.json())
.then(data => console.log('Success:', data))
.catch(err => console.error('Failed after all retries:', err.message));If running a Node.js server, configure proper timeout values to prevent socket hang ups on the client side:
const http = require('http');
const server = http.createServer((req, res) => {
// Simulate processing
setTimeout(() => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: true }));
}, 100);
});
// Configure timeouts on the server
server.keepAliveTimeout = 65000; // 65 seconds
server.headersTimeout = 66000; // 66 seconds (slightly more than keepAliveTimeout)
server.timeout = 120000; // 2 minutes overall timeout
// Handle client connection timeouts
server.on('clientError', (err, socket) => {
if (err.code === 'ECONNRESET' || err.message === 'socket hang up') {
console.log('Client socket hang up detected');
if (socket.writable) {
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
}
} else if (err.code !== 'EPIPE') {
console.error('Unexpected client error:', err);
socket.end('HTTP/1.1 500 Internal Server Error\r\n\r\n');
}
});
server.listen(3000, () => {
console.log('Server running on port 3000');
});For Express:
const express = require('express');
const app = express();
app.get('/api/data', (req, res) => {
res.json({ data: 'example' });
});
const server = app.listen(3000, () => {
console.log('Server running on port 3000');
});
// Configure timeouts
server.keepAliveTimeout = 65000;
server.headersTimeout = 66000;
// Handle socket errors
server.on('clientError', (err, socket) => {
if (err.message === 'socket hang up') {
console.log('Client hung up');
}
});For repeated requests to the same server, use a connection pooling library to maintain stable connections:
npm install agentkeepaliveconst http = require('http');
const https = require('https');
const Agent = require('agentkeepalive');
// Create agents with proper pooling
const httpAgent = new Agent({
keepAlive: true,
maxSockets: 100,
maxFreeSockets: 10,
timeout: 60000,
freeSocketTimeout: 30000,
keepAliveTimeout: 60000,
socketActiveTTL: 110000,
});
const httpsAgent = new Agent.HttpsAgent({
keepAlive: true,
maxSockets: 100,
maxFreeSockets: 10,
timeout: 60000,
freeSocketTimeout: 30000,
keepAliveTimeout: 60000,
socketActiveTTL: 110000,
});
// Use with axios or node-fetch
const axios = require('axios');
const client = axios.create({
httpAgent,
httpsAgent,
timeout: 30000,
});
// Now make requests - socket hang ups should be rare
client.get('https://api.example.com/data')
.then(res => console.log('Success'))
.catch(err => console.error('Error:', err.message));Socket Hang Up vs ECONNRESET:
While often shown with ECONNRESET error code, "socket hang up" and ECONNRESET are slightly different. ECONNRESET is a forced TCP reset, while socket hang up is a graceless termination or timeout. Both should be handled identically - with retries and proper timeout configuration.
Node.js Version Considerations:
Node.js versions 19+ have had reported issues with consecutive HTTP requests causing socket hang up errors, particularly with node-fetch. If experiencing persistent issues, check your Node.js version and consider upgrading or downgrading to find a stable version for your use case.
Keep-Alive Connection Lifecycle:
Socket hang up errors are most common with HTTP keep-alive enabled. Servers close idle keep-alive connections after a timeout (typically 5-65 seconds). If a client tries to reuse a socket just after the server has closed it, a socket hang up occurs. The solution is configuring timeouts so the client closes idle sockets slightly before the server does.
Network Intermediaries:
Proxies, load balancers, firewalls, and CDNs can cause socket hang ups by enforcing their own timeout rules. AWS ALB defaults to 60-second idle timeout, while many corporate firewalls reset connections after 30-60 seconds. In restricted networks, use shorter timeout values and implement aggressive retry logic.
Long-Running Operations:
For operations that take longer than typical socket timeouts (file uploads, downloads, streaming, database operations), explicitly increase the timeout and consider splitting the operation into smaller chunks. Long requests should have heartbeat mechanisms to keep the connection alive.
Debugging Socket Hang Ups:
Enable Node.js HTTP debugging with NODE_DEBUG=http to see the raw HTTP protocol exchange. Use network tools like tcpdump, Wireshark, or strace to see when and why the connection closes. Correlate client-side socket hang up errors with server-side logs to determine which side initiated the closure.
Cloud Environment Quirks:
Serverless and containerized environments often have aggressive network timeouts. AWS Lambda has a 900-second execution timeout but may have stricter socket timeouts. Docker containers may inherit host network timeout settings. When deploying to cloud, test socket hang up scenarios thoroughly.
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