can1357/oh-my-pi · error
Daemon ${operation.name} stdin is unavailable
Error message
Daemon ${operation.name} stdin is unavailable What it means
When a send operation includes data, the broker writes it to the daemon's PTY if one exists, otherwise to its captured stdin stream. If the daemon was started without a PTY and without stdin capture (record.pty and record.input are both unset), there is no writable input channel and the broker throws this error rather than silently dropping the data.
Source
Thrown at packages/coding-agent/src/launch/broker.ts:1124
const timedOut = operation.for === "ready" && !pattern ? !readyObserved() : !woke;
return { op: "wait", daemon: record.snapshot, matched, timedOut };
}
async #send(operation: Extract<DaemonOperation, { op: "send" }>): Promise<DaemonRpcResult> {
const record = this.#record(operation.name);
await this.#refreshDetached(record);
if (terminalState(record.snapshot.state) || record.snapshot.state === "stopping") {
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;View on GitHub (pinned to 9690622007)
Solutions
- Start the daemon with a PTY (or stdin capture) enabled so input can be delivered.
- Restart the daemon with the correct input configuration, then re-send.
- If the daemon doesn't need input, remove the send-data call instead of relying on stdin.
Example fix
// before
await broker.start({ name: "repl", cwd, command: "node", args: ["repl.js"] }); // no pty/stdin
await broker.send({ name: "repl", data: "1+1\n" }); // throws
// after
await broker.start({ name: "repl", cwd, command: "node", args: ["repl.js"], pty: true });
await broker.send({ name: "repl", data: "1+1\n" }); Defensive patterns
Strategy: validation
Validate before calling
// At launch time, ensure an input channel exists:
const spec = { name, cwd, command, args, pty: true }; // or configure stdin capture
// Before sending:
const snap = await broker.get(name);
if (snap && !snap.hasPty && !snap.hasStdin) throw new Error(`${name} has no input channel`); Try / catch
try {
await broker.send({ name, data });
} catch (err) {
if (err instanceof Error && err.message.endsWith("stdin is unavailable")) {
// restart daemon with pty/stdin enabled, then resend
} else throw err;
} Prevention
- Always launch interactive daemons with pty: true (or stdin capture) in the spec.
- Keep launch configuration and send expectations in one place so dropping pty breaks visibly at startup.
- Document which daemons accept input; treat input-less daemons as fire-and-forget.
When it happens
Trigger: Sending data to a daemon launched without pty: true and with stdin not piped/captured — the record has no input handle, so writes are impossible.
Common situations: Starting a detached daemon that doesn't allocate a PTY or stdin pipe, then later trying to feed it interactive input; changing launch flags (dropping pty) while clients still assume interactive stdin.
Related errors
- A detached daemon cannot allocate a PTY
- Windows batch files require application "cmd.exe" with the b
- Daemon broker environment is incomplete
- Overlapping replacements detected; refine pattern to avoid a
- when reading file names from standard input, no file name of
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/1272d9cbbed1ff08.
Report an issue: GitHub.