can1357/oh-my-pi · error · Error
Failed to inspect ${holder.image} (PID ${holder.pid})
Error message
Failed to inspect ${holder.image} (PID ${holder.pid}) What it means
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.
Source
Thrown at packages/stats/src/port-conflict.ts:208
if (process.platform === "win32") return findWindowsPortHolder(port);
return null;
}
async function terminatePortHolder(holder: PortHolder): Promise<void> {
try {
process.kill(holder.pid, "SIGTERM");
} catch (error) {
if (error instanceof Error && "code" in error && error.code === "ESRCH") return;
throw new Error(`Failed to stop ${holder.image} (PID ${holder.pid})`, { cause: error });
}
for (let attempt = 0; attempt < PROCESS_EXIT_POLLS; attempt++) {
await Bun.sleep(PROCESS_EXIT_POLL_MS);
try {
process.kill(holder.pid, 0);
} catch (error) {
if (error instanceof Error && "code" in error && error.code === "ESRCH") return;
throw new Error(`Failed to inspect ${holder.image} (PID ${holder.pid})`, { cause: error });
}
}
try {
process.kill(holder.pid, "SIGKILL");
} catch (error) {
if (error instanceof Error && "code" in error && error.code === "ESRCH") return;
throw new Error(`Failed to kill ${holder.image} (PID ${holder.pid})`, { cause: error });
}
await Bun.sleep(PROCESS_EXIT_POLL_MS);
}
async function reclaimStatsPort(port: number): Promise<"retry"> {
const holder = await findPortHolder(port);
if (!holder) {
throw new Error(`Port ${port} is in use, but the listening process could not be identified.`);
}
if (holder.pid === process.pid) {View on GitHub (pinned to 9690622007)
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
Example fix
// before
await recoverStatsPort(port); // throws 'Failed to inspect ... (PID 1234)'
// after
try {
await recoverStatsPort(port);
} catch (err) {
if (/Failed to inspect/.test(err.message)) {
console.error(`Cannot probe PID; stop it manually: sudo kill <pid>, or use OMP_STATS_PORT to pick another port`);
} else throw err;
} Defensive patterns
Strategy: try-catch
Validate before calling
try {
process.kill(holder.pid, 0);
console.log(`PID ${holder.pid} probe OK`);
} catch (err) {
if (err.code === 'EPERM') console.error('kill(0) probe denied — sandbox/permission issue ahead');
} Type guard
function isNodeErrno(err: unknown): err is NodeJS.ErrnoException {
return err instanceof Error && typeof (err as NodeJS.ErrnoException).code === 'string';
} Try / catch
try {
await recoverStatsPort(port);
} catch (err) {
if (err.message.startsWith('Failed to inspect')) {
console.error(`Cannot probe PID ${err.message}; verify with ps and stop manually if needed`);
} else throw err;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Failed to stop ${holder.image} (PID ${holder.pid})
- Failed to kill ${holder.image} (PID ${holder.pid})
- Daemon ${operation.name} process is unavailable
- Port ${port} is held by the current process (${holder.image}
- Port ${port} is in use by ${holder.image} (PID ${holder.pid}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/acd4dbd2550954c3.
Report an issue: GitHub.