can1357/oh-my-pi · error
Daemon ${operation.name} process is unavailable
Error message
Daemon ${operation.name} process is unavailable What it means
To deliver a signal on non-Windows platforms (or Windows without a PTY), the broker resolves the daemon's recorded pid via Process.fromPid and kills its process tree. If the snapshot has no pid, or the pid could not be resolved to a live process handle, the broker throws this error instead of signaling blindly.
Source
Thrown at packages/coding-agent/src/launch/broker.ts:1132
throw new Error(`Daemon ${operation.name} is ${record.snapshot.state}`);
}
if (operation.data === undefined && operation.signal === undefined) {
throw new Error("send requires data or signal");
}
if (operation.data !== undefined) {
if (record.pty) record.pty.write(operation.data);
else if (record.input) {
record.input.write(operation.data);
await record.input.flush();
} else throw new Error(`Daemon ${operation.name} stdin is unavailable`);
}
if (operation.signal) {
if (process.platform === "win32" && record.pty) {
if (operation.signal === "SIGINT") record.pty.write("\u0003");
else record.pty.kill();
} else {
const processRef = record.snapshot.pid === undefined ? null : Process.fromPid(record.snapshot.pid);
if (!processRef) throw new Error(`Daemon ${operation.name} process is unavailable`);
processRef.killTree(SIGNAL_NUMBER[operation.signal]);
}
}
return { op: "send", daemon: record.snapshot };
}
async #stopRecord(record: ManagedDaemon, timeoutMs: number): Promise<void> {
await this.#refreshDetached(record);
if (terminalState(record.snapshot.state)) return;
record.stopRequested = true;
if (record.restartTimer) {
clearTimeout(record.restartTimer);
record.restartTimer = undefined;
record.snapshot.state = "exited";
record.snapshot.exitedAt = Date.now();
this.#persist(record);
await record.log?.close();
record.log = undefined;View on GitHub (pinned to 9690622007)
Solutions
- Refresh the daemon state (list/get) and confirm the pid before sending signals.
- Use the broker's stop operation instead of a raw signal if you just want the daemon to terminate.
- If the daemon is already gone, treat this as already-stopped: clear the record and move on.
Example fix
// before
await broker.send({ name: "worker", signal: "SIGTERM" }); // pid missing -> throws
// after
const snap = await broker.get("worker");
if (snap?.pid !== undefined) {
await broker.send({ name: "worker", signal: "SIGTERM" });
} else {
await broker.stop({ name: "worker" });
} Defensive patterns
Strategy: validation
Validate before calling
const snap = await broker.get(name);
if (!snap || snap.pid === undefined) throw new Error(`${name} has no pid; use stop instead of signals`); Type guard
function hasPid(s: { pid?: number }): s is { pid: number } {
return typeof s.pid === "number";
} Try / catch
try {
await broker.send({ name, signal: "SIGTERM" });
} catch (err) {
if (err instanceof Error && err.message.endsWith("process is unavailable")) {
// treat as already-stopped or fall back to broker.stop
await broker.stop({ name });
} else throw err;
} Prevention
- Prefer the broker's stop operation over raw signal sends for termination.
- Refresh the snapshot (get/list) before signaling detached daemons.
- Handle broker restarts: re-attach and verify pid presence before signaling.
When it happens
Trigger: Sending a signal to a daemon whose snapshot lacks a pid (never observed or lost across broker restart with a detached record) or whose pid is no longer resolvable as a process handle.
Common situations: Daemon already reaped but state not yet refreshed; broker restarted and reattached to a detached daemon whose pid record is missing; pid reuse/exit raced with the signal send.
Related errors
- Failed to stop ${holder.image} (PID ${holder.pid})
- Failed to inspect ${holder.image} (PID ${holder.pid})
- Failed to kill ${holder.image} (PID ${holder.pid})
- Overlapping replacements detected; refine pattern to avoid a
- Computed edit range is out of bounds
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/0d9d0982e71466e8.
Report an issue: GitHub.