{"record":{"id":"628b59ca55b4c64b","repo":"mihomo-party-org/clash-party","slug":"core-pid-proc-pid-unknown-is-still-running","errorCode":null,"errorMessage":"Core PID ${proc.pid ?? 'unknown'} is still running after SIGKILL","messagePattern":"Core PID (.+?) is still running after SIGKILL","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/main/core/manager.ts","lineNumber":875,"sourceCode":"\nsetStopCoreBeforeAdminRestart(stopCore)\n\nasync function ensureCoreProcessExited(proc: ChildProcess | null): Promise<void> {\n  if (!proc) return\n\n  const waitForExit = async (): Promise<boolean> => {\n    const deadline = Date.now() + coreShutdownTimeout\n    while (proc.exitCode === null && proc.signalCode === null && Date.now() < deadline) {\n      await delay(50)\n    }\n    return proc.exitCode !== null || proc.signalCode !== null\n  }\n\n  if (await waitForExit()) return\n  managerLogger.warn(`Core PID ${proc.pid ?? 'unknown'} did not exit after SIGINT; sending SIGKILL`)\n  proc.kill('SIGKILL')\n  if (!(await waitForExit())) {\n    throw new Error(`Core PID ${proc.pid ?? 'unknown'} is still running after SIGKILL`)\n  }\n}\n\nasync function restartCoreOnce(forceStop: boolean): Promise<void> {\n  const startAttempt = await runCoreOperation(async () => {\n    const previousChild = child\n    await stopCoreInternal(forceStop)\n    if (process.platform === 'darwin') await ensureCoreProcessExited(previousChild)\n    return startCoreInternal(false, true)\n  })\n  await startAttempt.readiness\n}\n\nfunction trackCoreRestart(operation: () => Promise<void>): Promise<void> {\n  if (pendingRestart) return pendingRestart\n\n  isRestarting = true\n  const restart = operation().finally(() => {","sourceCodeStart":857,"sourceCodeEnd":893,"githubUrl":"https://github.com/mihomo-party-org/clash-party/blob/911e090537acdf7c50bee1c3aebecc2ef119a8b5/src/main/core/manager.ts#L857-L893","documentation":"This error is thrown in the core stop path (src/main/core/manager.ts:875) after SIGINT failed to stop the core process and a follow-up SIGKILL also failed: waitForExit() still reports the child running. The library throws because it cannot guarantee the previous core process is dead, which would make a subsequent start unreliable (port conflicts, stale state).","triggerScenarios":"stopCoreInternal sends SIGINT, the core ignores it, SIGKILL is sent, and waitForExit() still finds the PID alive after the timeout — typically a stuck or zombie child, PID reuse, or a process in uninterruptible kernel state (D state).","commonSituations":"Core binary hung on I/O or a dead lock making it un-killable; container/permission issues where SIGKILL cannot reach the process; PID reused by another process; extremely slow exit exceeding the wait timeout.","solutions":["Investigate why the core ignores SIGKILL: check process state (ps/proc), permissions, containers, or I/O hangs","Increase the waitForExit timeout if exits are merely slow","Escalate externally (kill -9 on the host, container restart) and verify the port is free before restarting the core","Report/log the stuck PID for diagnosis rather than silently starting a new core over the old one"],"exampleFix":"// before\nif (!(await waitForExit())) {\n  throw new Error(`Core PID ${proc.pid ?? 'unknown'} is still running after SIGKILL`)\n}\n// after\nif (!(await waitForExit())) {\n  managerLogger.error(`Core PID ${proc.pid ?? 'unknown'} unkillable; state=${proc.signalCode}`)\n  throw new CoreProcessUnkillableError(proc.pid)\n}","handlingStrategy":"retry","validationCode":"const stale = await isPidAlive(proc.pid)\nif (stale) {\n  await killProcessTree(proc.pid, { force: true })\n  await waitForPortFree(corePort)\n}","typeGuard":null,"tryCatchPattern":"try {\n  await stopCore()\n} catch (err) {\n  if (err.message.includes('still running after SIGKILL')) {\n    await escalateExternalKill(proc.pid)\n    await waitForPortFree(corePort)\n    await startCore()\n  } else throw err\n}","preventionTips":["Monitor core process health so hangs are detected before stop","Verify the core port is free before restarting after a failed kill","Run the core with adequate permissions (not in a restricted container that blocks signals)","Keep the stop timeout generous enough for slow exits"],"tags":["process-management","signal","sigkill","timeout","core-manager"],"backgroundTag":"process-wont-terminate-after-sigkill","analyzedSha":"911e090537acdf7c50bee1c3aebecc2ef119a8b5","analyzedAt":"2026-08-30T13:00:49.174Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}