What happened?
When the child process spawned by RpcClient dies (crash, OOM, kill), in-flight client.send() calls don't notice. They sit waiting for a reply until the 30s timer trips, then reject with "Timeout waiting for response..." — no hint that the child has been dead the whole time.
Also: any stdin.write() to the dead pipe during that window throws EPIPE asynchronously and can crash the parent if unhandled.
stop() works fine — that path drives the exit itself. Crash exits are the gap.
Steps to reproduce
import { RpcClient } from "@earendil-works/pi-coding-agent/rpc";
const client = new RpcClient({ agentPath: "/bin/false" }); // exits immediately
await client.start();
const t0 = Date.now();
try { await client.send({ type: "ping" }); } catch (e) {
console.log(Date.now() - t0, e.message); // ~30000, "Timeout waiting for response..."
}
Expected behavior
Reject in <100ms with the actual exit code/signal/stderr. Don't let EPIPE on the dead stdin escape.
Version
@earendil-works/pi-coding-agent 0.75.3.
Fix: one listener in start(), after attachJsonlLineReader:
this.process.on("exit", (code, signal) => {
const err = new Error(`Agent process exited (code=${code} signal=${signal}). Stderr: ${this.stderr}`);
for (const { reject } of this.pendingRequests.values()) reject(err);
this.pendingRequests.clear();
});
this.process.stdin?.on("error", () => { /* exit handler settles pending */ });
What happened?
When the child process spawned by
RpcClientdies (crash, OOM, kill), in-flightclient.send()calls don't notice. They sit waiting for a reply until the 30s timer trips, then reject with"Timeout waiting for response..."— no hint that the child has been dead the whole time.Also: any
stdin.write()to the dead pipe during that window throws EPIPE asynchronously and can crash the parent if unhandled.stop()works fine — that path drives the exit itself. Crash exits are the gap.Steps to reproduce
Expected behavior
Reject in <100ms with the actual exit code/signal/stderr. Don't let EPIPE on the dead stdin escape.
Version
@earendil-works/pi-coding-agent0.75.3.Fix: one listener in
start(), afterattachJsonlLineReader: