This error occurs when worker processes in a Node.js cluster module are trying to emit 'listening' events but no actual listening server exists in the worker processes. The cluster module expects workers to have a net.Server instance listening on a port to handle incoming connections.
The Node.js cluster module is designed to distribute incoming connections across multiple worker processes. When a worker calls `.listen()` on a server object (like an HTTP server), it signals to the cluster module that it has a listening server ready to accept connections. If you see this error, it means your code structure is incorrect. The cluster module expects: 1. Workers to have actual net.Server or http.Server instances calling `.listen(port)` 2. The primary process to fork workers (not listen itself) The error typically indicates that either: - Worker processes do not call .listen() on any server instance - The primary process is attempting to listen when it should only fork workers - You're using cluster with a non-network service (message queue consumer) that doesn't create listening servers
The primary process should fork workers, while workers should create and listen on servers. Check that your code follows this pattern:
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isPrimary) {
// PRIMARY: Fork workers, don't listen
console.log('Primary process starting...');
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log('Worker died, restarting...');
cluster.fork();
});
} else {
// WORKER: Create and listen on server
const server = http.createServer((req, res) => {
res.writeHead(200);
res.end('Hello from worker ' + process.pid);
});
server.listen(8000, () => {
console.log('Worker listening on port 8000');
});
}The key rule: only workers should call .listen() on servers, not the primary.
The cluster module specifically requires that workers have an actual listening server. Generic event emitters or message queue consumers without a listening server won't work.
Valid server types:
- http.createServer() - HTTP server
- https.createServer() - HTTPS server
- net.createServer() - Raw TCP server
Invalid patterns (will cause this error):
// WRONG: No server listening in workers
if (!cluster.isPrimary) {
const consumer = createRabbitMQConsumer(); // Not a listening server
consumer.on('message', handler);
// Error: No listening server, cluster.on('listening') never fires
}
// WRONG: Primary listening instead of workers
if (cluster.isPrimary) {
const server = http.createServer(...);
server.listen(8000); // Should be in worker block
cluster.fork();
}
// CORRECT: Workers have listening servers
if (!cluster.isPrimary) {
const server = http.createServer(...);
server.listen(8000); // Workers listen, primary doesn't
}If you need clustering for background job workers or message consumers (not web servers), don't rely on the cluster 'listening' event:
const cluster = require('cluster');
const os = require('os');
if (cluster.isPrimary) {
// Fork workers for background processing
for (let i = 0; i < os.cpus().length; i++) {
cluster.fork();
}
cluster.on('exit', (worker) => {
console.log('Worker died, restarting...');
cluster.fork(); // Restart dead workers
});
// Don't use cluster.on('listening') for non-server workloads
} else {
// Worker: Create a message consumer, NOT a listening server
const consumer = new RabbitMQConsumer();
consumer.start()
.then(() => console.log('Consumer started'))
.catch((err) => {
console.error('Consumer failed:', err);
process.exit(1);
});
}Create a simple test file to verify clustering works with a listening server:
// test-cluster.js
const cluster = require('cluster');
const http = require('http');
const os = require('os');
const PORT = process.env.PORT || 8000;
if (cluster.isPrimary) {
console.log('Primary process ' + process.pid + ' starting...');
// Fork workers
for (let i = 0; i < os.cpus().length; i++) {
cluster.fork();
}
// Listen for 'listening' event from workers
cluster.on('listening', (worker, address) => {
console.log('Worker ' + worker.process.pid + ' listening on port ' + address.port);
});
cluster.on('exit', (worker, code, signal) => {
console.log('Worker ' + worker.process.pid + ' exited');
cluster.fork(); // Respawn
});
} else {
// Worker: Create a listening server
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Response from worker ' + process.pid + '\n');
});
server.listen(PORT, () => {
console.log('Worker ' + process.pid + ' listening on port ' + PORT);
});
}Run with: node test-cluster.js and visit http://localhost:8000
Cluster module behavior changed over Node.js versions. Ensure you're using cluster.isPrimary (Node 16+) instead of the older cluster.isMaster:
// Modern (Node 16+)
if (cluster.isPrimary) {
// Fork workers
} else {
// Worker code
}
// Older versions (deprecated)
if (cluster.isMaster) {
// This still works but is deprecated
}If you're on an older Node.js version, update to Node 18 LTS or later for better cluster support and to avoid deprecated APIs.
Round-robin vs. Shared Socket Handles: By default, Node.js uses round-robin load balancing on non-Windows platforms, where the primary process coordinates connection distribution to workers. On Windows, file handles are shared directly with workers. Both require workers to call .listen() to properly emit the 'listening' event that the cluster module expects and to receive connections.
Message Queue and Background Job Services: If you're building a worker pool for background jobs (Kafka, RabbitMQ, Bull queue, etc.), you may not need the cluster module at all. The cluster module is specifically designed for distributing network connections. Consider using dedicated worker libraries like bull, node-resque, bee-queue, or worker_threads for better isolation and control.
Sticky Sessions: If you're using cluster for HTTP servers, note that load balancing is per-connection, not per-request. Client sessions may be distributed across different workers. Use a shared session store (Redis) or sticky routing if session persistence matters for your application.
Graceful Shutdown: Always implement graceful shutdown handlers in workers. Use process.on('SIGTERM') or process.on('SIGINT') to close servers before exiting, allowing in-flight requests to complete.
Error: EMFILE: too many open files, watch
EMFILE: fs.watch() limit exceeded
Error: Middleware next() called multiple times (next() invoked twice)
Express middleware next() called multiple times
Error: Worker failed to initialize (worker startup error)
Worker failed to initialize in Node.js
Error: EMFILE: too many open files, open 'file.txt'
EMFILE: too many open files
Error: cluster.fork() failed (cannot create child process)
cluster.fork() failed - Cannot create child process