denoland/deno · error · NodeError

ERR_IPC_DISCONNECTED

ERR_IPC_DISCONNECTED

Error message

IPC channel is already disconnected

What it means

disconnect() throws ERR_IPC_DISCONNECTED when target.connected is already false — meaning disconnect() was called twice, or the other side (or child exit) already closed the channel. The flag flips synchronously on the first call, so the second call is the one that throws. Node uses the same code and message.

Source

Thrown at ext/node/polyfills/internal/child_process.ts:2944

    let handleInfo = null;
    if (handle !== undefined) {
      handleInfo = getIpcHandleInfo(handle, options, target);
      // `getIpcHandleInfo` returns null when the handle has no underlying
      // native handle (e.g. a server that never started listening). Match
      // Node, which strips the handle and sends the plain message instead.
      if (handleInfo !== null) {
        handleInfo.message.msg = message;
      }
    }

    return enqueueOrDispatch(message, handleInfo, callback);
  };

  target.connected = true;

  target.disconnect = function () {
    if (!target.connected) {
      throw new ERR_IPC_DISCONNECTED();
    }

    target.connected = false;
    target[kCanDisconnect] = false;
    cleanupPendingHandles();
    control[kControlDisconnect]();
    nextTick(() => {
      target.channel = null;
      core.close(ipc);
      target.emit("disconnect");
    });
  };
  target[kCanDisconnect] = true;

  // Start reading messages from the channel.
  readLoop();

  return control;

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Guard the call: if (child.connected) child.disconnect() — or process.connected in a worker
  2. Make teardown idempotent with a single shutdown flag and one registration point
  3. Treat the 'disconnect' event as the authoritative signal instead of re-calling disconnect

Example fix

// before
function shutdown() {
  child.disconnect();
}
process.on("SIGTERM", shutdown);
child.on("exit", shutdown); // second call throws

// after
let disconnected = false;
function shutdown() {
  if (disconnected || !child.connected) return;
  disconnected = true;
  child.disconnect();
}
Defensive patterns

Strategy: validation

Validate before calling

if (child.connected) {
  child.disconnect();
}
// in a worker: if (process.connected) process.disconnect();

Try / catch

try {
  child.disconnect();
} catch (err) {
  if (err.code !== "ERR_IPC_DISCONNECTED") throw err;
  // channel already gone; treat as success during shutdown
}

Prevention

When it happens

Trigger: Calling child.disconnect() twice; calling process.disconnect() in a worker after the primary already disconnected; disconnect() reached from both a signal handler and an exit handler.

Common situations: Shutdown code that runs more than once (SIGTERM plus beforeExit); races between parent teardown and worker disconnect logic; code that ignores the 'disconnect' event and re-calls disconnect defensively.

Related errors


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