Skip to content

Commit ec64376

Browse files
committed
fix(codex): close app-server client on stdio stream errors
1 parent 48e77b6 commit ec64376

2 files changed

Lines changed: 35 additions & 0 deletions

File tree

extensions/codex/src/app-server/client.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,32 @@ describe("CodexAppServerClient", () => {
422422
await expect(harness.client.request("another/method")).rejects.toThrow("write EPIPE");
423423
});
424424

425+
it("handles stdout stream errors without crashing the process", async () => {
426+
const harness = createClientHarness();
427+
clients.push(harness.client);
428+
429+
const pending = harness.client.request("test/method");
430+
const readError = Object.assign(new Error("stdout pipe broke"), { code: "EIO" });
431+
432+
expect(() => harness.process.stdout.emit("error", readError)).not.toThrow();
433+
434+
await expect(pending).rejects.toThrow("stdout pipe broke");
435+
await expect(harness.client.request("another/method")).rejects.toThrow("stdout pipe broke");
436+
});
437+
438+
it("handles stderr stream errors without crashing the process", async () => {
439+
const harness = createClientHarness();
440+
clients.push(harness.client);
441+
442+
const pending = harness.client.request("test/method");
443+
const stderrError = Object.assign(new Error("stderr pipe broke"), { code: "EIO" });
444+
445+
expect(() => harness.process.stderr.emit("error", stderrError)).not.toThrow();
446+
447+
await expect(pending).rejects.toThrow("stderr pipe broke");
448+
await expect(harness.client.request("another/method")).rejects.toThrow("stderr pipe broke");
449+
});
450+
425451
it("preserves redacted app-server stderr on exit errors", async () => {
426452
const harness = createClientHarness();
427453
clients.push(harness.client);

extensions/codex/src/app-server/client.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,12 @@ export class CodexAppServerClient {
139139
this.child = child;
140140
this.lines = createInterface({ input: child.stdout });
141141
this.lines.on("line", (line) => this.handleLine(line));
142+
this.lines.on("error", (error) =>
143+
this.closeWithError(error instanceof Error ? error : new Error(String(error))),
144+
);
145+
child.stdout.on("error", (error) =>
146+
this.closeWithError(error instanceof Error ? error : new Error(String(error))),
147+
);
142148
child.stderr.on("data", (chunk: Buffer | string) => {
143149
const text = chunk.toString("utf8");
144150
this.stderrTail = appendBoundedTail(this.stderrTail, text, CODEX_APP_SERVER_STDERR_TAIL_MAX);
@@ -147,6 +153,9 @@ export class CodexAppServerClient {
147153
embeddedAgentLog.debug(`codex app-server stderr: ${trimmed}`);
148154
}
149155
});
156+
child.stderr.on("error", (error) =>
157+
this.closeWithError(error instanceof Error ? error : new Error(String(error))),
158+
);
150159
child.once("error", (error) =>
151160
this.closeWithError(error instanceof Error ? error : new Error(String(error))),
152161
);

0 commit comments

Comments
 (0)