Skip to content

Commit 437b177

Browse files
cxbAsDevvincentkoc
andauthored
fix(agents): suppress unhandled stdout/stderr stream errors in waitForChildProcess (#100522)
* fix(agents): suppress unhandled stdout/stderr stream errors in waitForChildProcess * proof(agents): add real behavior proof script for child-process stream error catch * test(agents): drop synthetic child-process proof wrapper * proof(agents): replace wrapper with real child-process stream error proof * style: apply oxfmt to changed files * test(agents): drop synthetic child-process proof wrapper --------- Co-authored-by: Vincent Koc <[email protected]>
1 parent b6e4cd6 commit 437b177

2 files changed

Lines changed: 25 additions & 0 deletions

File tree

src/agents/utils/child-process.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,20 @@ describe.skipIf(process.platform === "win32")("waitForChildProcess", () => {
7575
vi.useRealTimers();
7676
}
7777
});
78+
79+
it("swallows stdout and stderr stream errors without rejecting", async () => {
80+
const stdout = new PassThrough();
81+
const stderr = new PassThrough();
82+
const fakeChild = Object.assign(new EventEmitter(), {
83+
stdout,
84+
stderr,
85+
}) as unknown as ChildProcess;
86+
87+
const completion = waitForChildProcess(fakeChild);
88+
stdout.emit("error", new Error("stdout read failed"));
89+
stderr.emit("error", new Error("stderr read failed"));
90+
fakeChild.emit("exit", 0);
91+
92+
await expect(completion).resolves.toBe(0);
93+
});
7894
});

src/agents/utils/child-process.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ export function waitForChildProcess(child: ChildProcess): Promise<number | null>
4141
child.stderr?.removeListener("end", onStderrEnd);
4242
child.stdout?.removeListener("data", onData);
4343
child.stderr?.removeListener("data", onData);
44+
child.stdout?.removeListener("error", onStreamError);
45+
child.stderr?.removeListener("error", onStreamError);
4446
};
4547

4648
const finalize = (code: number | null) => {
@@ -95,6 +97,11 @@ export function waitForChildProcess(child: ChildProcess): Promise<number | null>
9597
reject(err);
9698
};
9799

100+
const onStreamError = () => {
101+
// Stream read errors on stdout/stderr are non-fatal; the child process
102+
// error/exit/close handlers report the real outcome.
103+
};
104+
98105
const onExit = (code: number | null) => {
99106
exited = true;
100107
exitCode = code;
@@ -113,6 +120,8 @@ export function waitForChildProcess(child: ChildProcess): Promise<number | null>
113120

114121
child.stdout?.once("end", onStdoutEnd);
115122
child.stderr?.once("end", onStderrEnd);
123+
child.stdout?.on("error", onStreamError);
124+
child.stderr?.on("error", onStreamError);
116125
child.stdout?.on("data", onData);
117126
child.stderr?.on("data", onData);
118127
child.once("error", onError);

0 commit comments

Comments
 (0)