{"record":{"id":"acd4dbd2550954c3","repo":"can1357/oh-my-pi","slug":"failed-to-inspect-holder-image-pid-holder-pi","errorCode":null,"errorMessage":"Failed to inspect ${holder.image} (PID ${holder.pid})","messagePattern":"Failed to inspect (.+?) \\(PID (.+?)\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/stats/src/port-conflict.ts","lineNumber":208,"sourceCode":"\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}\n\tawait Bun.sleep(PROCESS_EXIT_POLL_MS);\n}\n\nasync function reclaimStatsPort(port: number): Promise<\"retry\"> {\n\tconst holder = await findPortHolder(port);\n\tif (!holder) {\n\t\tthrow new Error(`Port ${port} is in use, but the listening process could not be identified.`);\n\t}\n\tif (holder.pid === process.pid) {","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/stats/src/port-conflict.ts#L190-L226","documentation":"After SIGTERM, terminatePortHolder() polls the PID with signal 0 (existence check). If a poll fails with an errno other than ESRCH, the error is wrapped as 'Failed to inspect'. This is distinct from a kill failure: the signal was sent, but the runtime cannot even probe whether the process still exists.","triggerScenarios":"During the exit-poll loop, process.kill(holder.pid, 0) throws EPERM or another non-ESRCH errno — typically when the SIGTERM changed the process's ownership/visibility context (e.g. it re-execs under a different user) or the environment restricts signal(0) probes.","commonSituations":"Containers or macOS seatbelt/profiles denying kill(0) probes; the holder process being re-parented to a different UID after a graceful restart; mismatched namespaces where the PID table is partially visible.","solutions":["Grant the probing user visibility/signal rights over the target (same user, same container/namespace)","Verify with ps -p <pid> whether the process actually exited despite the probe failing","Free the port without termination: choose a different port or stop the holder manually","Update permissions/sandbox config so kill(pid, 0) checks are allowed"],"exampleFix":"// before\nawait recoverStatsPort(port); // throws 'Failed to inspect ... (PID 1234)'\n// after\ntry {\n  await recoverStatsPort(port);\n} catch (err) {\n  if (/Failed to inspect/.test(err.message)) {\n    console.error(`Cannot probe PID; stop it manually: sudo kill <pid>, or use OMP_STATS_PORT to pick another port`);\n  } else throw err;\n}","handlingStrategy":"try-catch","validationCode":"try {\n  process.kill(holder.pid, 0);\n  console.log(`PID ${holder.pid} probe OK`);\n} catch (err) {\n  if (err.code === 'EPERM') console.error('kill(0) probe denied — sandbox/permission issue ahead');\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 recoverStatsPort(port);\n} catch (err) {\n  if (err.message.startsWith('Failed to inspect')) {\n    console.error(`Cannot probe PID ${err.message}; verify with ps and stop manually if needed`);\n  } else throw err;\n}","preventionTips":["Ensure sandbox/container profiles permit kill(pid, 0) probes against the holder","Keep holder and reclaimer in the same user/namespace context","If probes fail, fall back to manual ps/netstat inspection rather than looping retries","Treat repeated inspect failures as an environment permission problem, not a code bug"],"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"}