This error occurs when the Node.js cluster module attempts to create a worker with an ID that already exists in the active worker pool. It typically happens when worker IDs are manually assigned or when the primary process tries to create duplicate workers without properly tracking existing worker IDs.
The Node.js cluster module maintains an internal registry of active worker processes, each identified by a unique numeric ID. When you call cluster.fork(), the module automatically assigns the next available ID to the new worker. This error indicates that code is either: (1) Attempting to manually assign an ID that conflicts with an existing worker, (2) Creating multiple fork() calls with duplicate configuration, or (3) Not properly cleaning up worker references when workers are expected to restart.
Never manually set worker IDs. Allow the cluster module to assign them automatically:
const cluster = require('cluster');
const http = require('http');
const os = require('os');
if (cluster.isPrimary) {
const numCPUs = os.cpus().length;
// CORRECT: Let cluster auto-assign IDs
for (let i = 0; i < numCPUs; i++) {
const worker = cluster.fork();
console.log(`Worker forked with ID: ${worker.id}`);
}
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.id} (PID ${worker.process.pid}) died`);
});
} else {
const server = http.createServer((req, res) => {
res.writeHead(200);
res.end(`Hello from worker ${cluster.worker.id}\n`);
});
server.listen(8000);
}The cluster.worker.id is read-only and assigned by the cluster module.
If you have code that tries to manually assign worker IDs, refactor it to use cluster's automatic ID management:
// WRONG: Attempting to manually set IDs
const workers = {};
for (let i = 0; i < 4; i++) {
const worker = cluster.fork();
worker.id = i; // ERROR: worker.id is read-only!
workers[i] = worker;
}
// CORRECT: Use auto-assigned IDs
const workers = {};
for (let i = 0; i < 4; i++) {
const worker = cluster.fork();
workers[worker.id] = worker; // Use the assigned ID
}Use the worker object's properties instead of trying to override the ID.
Wait for the 'exit' event before forking a replacement worker:
const cluster = require('cluster');
if (cluster.isPrimary) {
// Track which workers we've already forked
const forkingWorkers = new Set();
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.id} exited`);
// Remove from tracking
forkingWorkers.delete(worker.id);
// Only fork replacement if not already doing so
if (!forkingWorkers.has(worker.id)) {
console.log(`Respawning worker to replace ${worker.id}`);
forkingWorkers.add(worker.id);
const newWorker = cluster.fork();
// The new worker will get the NEXT available ID, not the old one
newWorker.on('online', () => {
console.log(`New worker ${newWorker.id} is online`);
});
}
});
// Initial worker creation
for (let i = 0; i < 2; i++) {
cluster.fork();
}
}Ensure that cluster.fork() is not being called multiple times from the same event:
const cluster = require('cluster');
let forking = false;
if (cluster.isPrimary) {
cluster.on('exit', (worker, code, signal) => {
// WRONG: No guard against multiple calls
// cluster.fork(); // Could be called multiple times
// CORRECT: Use a guard to prevent duplicate forks
if (!forking && code !== 0 && !worker.exitedAfterDisconnect) {
forking = true;
const newWorker = cluster.fork();
newWorker.on('online', () => {
console.log(`Worker ${newWorker.id} online`);
forking = false; // Reset guard
});
// Timeout in case worker never comes online
setTimeout(() => {
forking = false;
}, 5000);
}
});
cluster.fork();
}Ensure your cluster setup code runs exactly once when the primary starts:
const cluster = require('cluster');
// CORRECT: Check isPrimary early
if (cluster.isPrimary) {
console.log(`Primary ${process.pid} started`);
// Initialization code runs once
for (let i = 0; i < 4; i++) {
cluster.fork();
}
cluster.on('exit', (worker) => {
console.log(`Worker ${worker.id} died`);
});
} else {
// Worker code
console.log(`Worker ${cluster.worker.id} started`);
}
// ✓ No cluster setup code here that might run twiceNever run cluster.fork() outside the if (cluster.isPrimary) block.
Add detailed logging to track worker ID assignment:
const cluster = require('cluster');
const http = require('http');
if (cluster.isPrimary) {
console.log(`Primary ${process.pid} starting`);
for (let i = 0; i < 2; i++) {
const worker = cluster.fork();
console.log(`✓ Forked worker with ID: ${worker.id}, PID: ${worker.process.pid}`);
}
cluster.on('online', (worker) => {
console.log(`✓ Worker ${worker.id} is online`);
});
cluster.on('exit', (worker, code, signal) => {
console.log(`✗ Worker ${worker.id} exited (code: ${code}, signal: ${signal})`);
});
// List active workers
setInterval(() => {
const workerIds = Object.keys(cluster.workers).map(id => cluster.workers[id].id);
console.log(`Active worker IDs: [${workerIds.join(', ')}]`);
}, 5000);
} else {
const server = http.createServer((req, res) => {
res.writeHead(200);
res.end(`Worker ${cluster.worker.id}\n`);
});
server.listen(3000 + cluster.worker.id);
console.log(`Worker ${cluster.worker.id} listening on port ${3000 + cluster.worker.id}`);
}Worker IDs in Node.js cluster module are assigned sequentially starting from 1 each time a new primary process starts. The internal worker registry is keyed by these IDs. If your application is trying to force specific IDs (which is not recommended), you'll encounter this error because IDs are immutable properties assigned by the cluster module itself. In some older Node.js versions or with certain pattern misuse, you might encounter race conditions where fork() is called before the previous worker is fully registered. The cluster module uses libuv's process management, which ensures ID uniqueness, so this error typically indicates a code logic issue rather than a cluster module bug. For complex worker management scenarios (dynamic scaling, load balancing), consider using dedicated cluster libraries like PM2, which handle worker ID tracking and graceful replacement automatically.
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