{"record":{"id":"b7ce67961a6a2d3b","repo":"can1357/oh-my-pi","slug":"failed-to-stop-holder-image-pid-holder-pid","errorCode":null,"errorMessage":"Failed to stop ${holder.image} (PID ${holder.pid})","messagePattern":"Failed to stop (.+?) \\(PID (.+?)\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/stats/src/port-conflict.ts","lineNumber":199,"sourceCode":"\tif (!powershell) return { pid, image, commandLine: \"\" };\n\tconst command = `(Get-CimInstance Win32_Process -Filter \"ProcessId = ${pid}\").CommandLine`;\n\tconst processInfo = await $`${powershell} -NoProfile -NonInteractive -Command ${command}`.quiet().nothrow();\n\treturn { pid, image, commandLine: processInfo.exitCode === 0 ? processInfo.text().trim() : \"\" };\n}\n\nasync function findPortHolder(port: number): Promise<PortHolder | null> {\n\tif (process.platform === \"linux\") return findLinuxPortHolder(port);\n\tif (process.platform === \"darwin\") return findMacPortHolder(port);\n\tif (process.platform === \"win32\") return findWindowsPortHolder(port);\n\treturn null;\n}\n\nasync function terminatePortHolder(holder: PortHolder): Promise<void> {\n\ttry {\n\t\tprocess.kill(holder.pid, \"SIGTERM\");\n\t} catch (error) {\n\t\tif (error instanceof Error && \"code\" in error && error.code === \"ESRCH\") return;\n\t\tthrow new Error(`Failed to stop ${holder.image} (PID ${holder.pid})`, { cause: error });\n\t}\n\n\tfor (let attempt = 0; attempt < PROCESS_EXIT_POLLS; attempt++) {\n\t\tawait Bun.sleep(PROCESS_EXIT_POLL_MS);\n\t\ttry {\n\t\t\tprocess.kill(holder.pid, 0);\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error && \"code\" in error && error.code === \"ESRCH\") return;\n\t\t\tthrow new Error(`Failed to inspect ${holder.image} (PID ${holder.pid})`, { cause: error });\n\t\t}\n\t}\n\n\ttry {\n\t\tprocess.kill(holder.pid, \"SIGKILL\");\n\t} catch (error) {\n\t\tif (error instanceof Error && \"code\" in error && error.code === \"ESRCH\") return;\n\t\tthrow new Error(`Failed to kill ${holder.image} (PID ${holder.pid})`, { cause: error });\n\t}","sourceCodeStart":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/stats/src/port-conflict.ts#L181-L217","documentation":"terminatePortHolder() in the stats package sends SIGTERM to the process occupying the stats port. If that kill(2) call fails with anything other than ESRCH (process already gone), the error is wrapped and rethrown with the holder's image name and PID. This means the runtime could not even deliver the signal — usually a permissions problem.","triggerScenarios":"process.kill(holder.pid, 'SIGTERM') throwing EPERM (process owned by another user), EINVAL, or any non-ESRCH errno while reclaiming the stats port via reclaimStatsPort.","commonSituations":"The port is held by a process started under a different user or via sudo; containerized setups where the PID is visible but not signalable; a stale/wrapped PID whose signal is denied by the OS; hardened sandbox blocking signals.","solutions":["Stop the offending process manually with elevated privileges (sudo kill <pid>)","Run the omp stats server as the same user that owns the port holder","Identify the holder (ps -p <pid>) and free the port another way, or point stats at a different port","If the process is in an uninterruptible state, resolve the underlying hang before reclaiming"],"exampleFix":"// before\nawait reclaimStatsPort(port); // throws 'Failed to stop omp (PID 1234)'\n// after\ntry {\n  await reclaimStatsPort(port);\n} catch (err) {\n  console.error(`Reclaim failed: ${err.message}; stop PID manually: sudo kill <pid>`);\n}","handlingStrategy":"try-catch","validationCode":"try {\n  process.kill(holder.pid, 0);\n} catch (err) {\n  if (err.code === 'EPERM') console.error(`PID ${holder.pid} not signalable by current user; elevated kill required`);\n}","typeGuard":"function isNodeErrno(err: unknown): err is NodeJS.ErrnoException {\n  return err instanceof Error && typeof (err as NodeJS.ErrnoException).code === 'string';\n}","tryCatchPattern":"try {\n  await reclaimStatsPort(port);\n} catch (err) {\n  if (err.message.startsWith('Failed to stop')) {\n    console.error(`${err.message} — stop it manually: sudo kill <pid>`);\n  } else throw err;\n}","preventionTips":["Run omp stats under the same user that owns the port-holder process","Avoid launching the previous stats server with sudo","Check ps -p <pid> ownership before attempting port reclamation","Prefer configuring a distinct port over killing unknown processes"],"tags":["process","signal","permissions","port"],"backgroundTag":"process-kill-eperm","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}