denoland/deno · error · Error
SharedHandle.add: worker already added
Error message
SharedHandle.add: worker already added
What it means
When schedulingPolicy is SCHED_NONE (or the platform uses shared sockets), the primary shares one listening handle among workers and registers each worker in SharedHandle.workers. add() throws a plain Error when the same worker.id registers twice for the same (address, port, addressType) query — the same duplicate-add invariant as the round-robin path, enforced in the shared-socket bookkeeping.
Source
Thrown at ext/node/polyfills/internal/cluster/shared_handle.ts:64
} else {
this.handle = rval;
// Match Node: leave listen() to the worker. We rely on `_createServerHandle`
// (and below it `uv_tcp_bind`) reporting EADDRINUSE synchronously, so the
// primary doesn't need to enter LISTEN state to discover bind errors.
// Keeping the primary out of LISTEN avoids it sitting on the kernel
// accept queue for the shared port (it never calls accept()) and avoids
// surprising interactions with later bind() calls in the primary process
// -- e.g. an ipv6Only listener on `::` blocking a sibling 0.0.0.0 bind.
}
}
SharedHandle.prototype.add = function (
this: any,
worker: any,
send: (errno: number, reply: null, handle: any) => void,
) {
if (this.workers.has(worker.id)) {
throw new Error("SharedHandle.add: worker already added");
}
this.workers.set(worker.id, worker);
send(this.errno, null, this.handle);
};
SharedHandle.prototype.remove = function (this: any, worker: any) {
if (!this.workers.has(worker.id)) {
return false;
}
this.workers.delete(worker.id);
if (this.workers.size !== 0) {
return false;
}
this.handle.close();
this.handle = null;View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Listen once per port per worker; consolidate handlers onto a single server instance
- Avoid reusing worker ids across fork cycles
- Reproduce with the default round-robin policy to confirm the root cause is a double listen
Example fix
// before const a = net.createServer(); const b = net.createServer(); a.listen(9000); b.listen(9000); // second listening message from the same worker // after const server = net.createServer(); server.listen(9000); // one listening server per port per worker
Defensive patterns
Strategy: validation
Validate before calling
const listening = new Set();
function listenOnce(server, port) {
if (listening.has(port)) {
throw new Error(`worker already listening on ${port}`);
}
listening.add(port);
server.listen(port);
} Prevention
- Consolidate handlers onto one server per port in each worker
- Do not re-listen on the same port after restart inside a live worker; fork fresh
- Test with both scheduling policies when adding multi-server workers
When it happens
Trigger: cluster.schedulingPolicy = cluster.SCHED_NONE with a worker that sends two listening messages for the same port (double listen() in one worker); duplicate worker ids from custom fork logic.
Common situations: Windows deployments or explicit SCHED_NONE with repeated listen() calls; test harnesses that start and restart servers repeatedly inside a long-lived worker.
Related errors
- RoundRobinHandle.add: worker already added
- Unexpected third argument to Deno.bench()
- The benchmark which this context belongs to is not being exe
- Bad cluster.schedulingPolicy: ${schedulingPolicy}
- Unknown worker event: "${type}"
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/7d6b20c9373dcaa8.
Report an issue: GitHub.