{"record":{"id":"fb6df708d0a85066","repo":"denoland/deno","slug":"err-ipc-disconnected","errorCode":"ERR_IPC_DISCONNECTED","errorMessage":"IPC channel is already disconnected","messagePattern":"IPC channel is already disconnected","errorType":"exception","errorClass":"NodeError","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/internal/child_process.ts","lineNumber":2944,"sourceCode":"    let handleInfo = null;\n    if (handle !== undefined) {\n      handleInfo = getIpcHandleInfo(handle, options, target);\n      // `getIpcHandleInfo` returns null when the handle has no underlying\n      // native handle (e.g. a server that never started listening). Match\n      // Node, which strips the handle and sends the plain message instead.\n      if (handleInfo !== null) {\n        handleInfo.message.msg = message;\n      }\n    }\n\n    return enqueueOrDispatch(message, handleInfo, callback);\n  };\n\n  target.connected = true;\n\n  target.disconnect = function () {\n    if (!target.connected) {\n      throw new ERR_IPC_DISCONNECTED();\n    }\n\n    target.connected = false;\n    target[kCanDisconnect] = false;\n    cleanupPendingHandles();\n    control[kControlDisconnect]();\n    nextTick(() => {\n      target.channel = null;\n      core.close(ipc);\n      target.emit(\"disconnect\");\n    });\n  };\n  target[kCanDisconnect] = true;\n\n  // Start reading messages from the channel.\n  readLoop();\n\n  return control;","sourceCodeStart":2926,"sourceCodeEnd":2962,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/internal/child_process.ts#L2926-L2962","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Guard the call: if (child.connected) child.disconnect() — or process.connected in a worker","Make teardown idempotent with a single shutdown flag and one registration point","Treat the 'disconnect' event as the authoritative signal instead of re-calling disconnect"],"exampleFix":"// before\nfunction shutdown() {\n  child.disconnect();\n}\nprocess.on(\"SIGTERM\", shutdown);\nchild.on(\"exit\", shutdown); // second call throws\n\n// after\nlet disconnected = false;\nfunction shutdown() {\n  if (disconnected || !child.connected) return;\n  disconnected = true;\n  child.disconnect();\n}","handlingStrategy":"validation","validationCode":"if (child.connected) {\n  child.disconnect();\n}\n// in a worker: if (process.connected) process.disconnect();","typeGuard":null,"tryCatchPattern":"try {\n  child.disconnect();\n} catch (err) {\n  if (err.code !== \"ERR_IPC_DISCONNECTED\") throw err;\n  // channel already gone; treat as success during shutdown\n}","preventionTips":["Always guard disconnect() with the connected flag","Register teardown exactly once; use an idempotent shutdown wrapper","Listen for the 'disconnect' event instead of re-calling disconnect defensively"],"tags":["child-process","ipc","lifecycle","cluster"],"backgroundTag":"ipc-channel-disconnected","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}