diff --git a/packages/plugins/agent-codex/src/app-server-client.test.ts b/packages/plugins/agent-codex/src/app-server-client.test.ts index 47eb7e991..c2b5db7ea 100644 --- a/packages/plugins/agent-codex/src/app-server-client.test.ts +++ b/packages/plugins/agent-codex/src/app-server-client.test.ts @@ -747,6 +747,28 @@ describe("CodexAppServerClient", () => { expect(client.isConnected).toBe(false); }); + it("rejects pending requests before emitting error (no listener crash safe)", async () => { + const proc = createFakeProcess(); + const client = new CodexAppServerClient({ requestTimeout: 5000 }); + + await connectClient(client, proc); + + // Do NOT attach an error listener — emit("error") will throw + // Start a request that will be pending + const requestPromise = client.threadList(); + await new Promise((r) => setTimeout(r, 10)); + + // Process emits error — without a listener, emit("error") throws. + // Pending requests must still be rejected before that throw. + try { + proc.emit("error", new Error("spawn ENOENT")); + } catch { + // Expected: uncaught error event from EventEmitter + } + + await expect(requestPromise).rejects.toThrow("spawn ENOENT"); + }); + it("handles malformed JSON lines gracefully", async () => { const proc = createFakeProcess(); const client = new CodexAppServerClient({ requestTimeout: 500 }); diff --git a/packages/plugins/agent-codex/src/app-server-client.ts b/packages/plugins/agent-codex/src/app-server-client.ts index 76eea7626..d8b165052 100644 --- a/packages/plugins/agent-codex/src/app-server-client.ts +++ b/packages/plugins/agent-codex/src/app-server-client.ts @@ -494,13 +494,14 @@ export class CodexAppServerClient extends EventEmitter { this.readline = null; } - this.emit("error", err); - - // Reject all pending requests + // Reject all pending requests before emitting "error" — emit("error") + // with no listeners throws synchronously, which would skip cleanup. for (const [id, pending] of this.pending) { clearTimeout(pending.timer); pending.reject(err); this.pending.delete(id); } + + this.emit("error", err); } }