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

  1. Listen once per port per worker; consolidate handlers onto a single server instance
  2. Avoid reusing worker ids across fork cycles
  3. 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

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


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/7d6b20c9373dcaa8. Report an issue: GitHub.