From 31ead4c36661379ccf10972cae8544f812664716 Mon Sep 17 00:00:00 2001 From: Noah Lindner Date: Wed, 9 Sep 2026 02:01:13 -0400 Subject: [PATCH] Turn timeout is a timer we clear; a turn that ends early no longer aborts a dead child ten minutes later AbortSignal.timeout kept firing after the SDK had removed its child listeners, so any turn ending on an error event crashed the process at the timeout (21:56 UTC restart). Co-Authored-By: Claude Fable 5.1 --- src/codex.ts | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/codex.ts b/src/codex.ts index 95effdd..bfc7c1d 100644 --- a/src/codex.ts +++ b/src/codex.ts @@ -58,20 +58,28 @@ export class Codex { } private async turn(thread: Thread, label: string, prompt: string, timeoutMs: number) { - const { events } = await thread.runStreamed(prompt, { signal: AbortSignal.timeout(timeoutMs) }); - for await (const event of events) { - if (event.type === "item.completed") { - const { item } = event; - if (item.type === "command_execution") log.info(label, { line: `$ ${item.command}` }); - else if (item.type === "mcp_tool_call") - log.info(label, { - line: `⚙ ${item.tool} ${JSON.stringify(item.arguments)}${item.error ? ` ✗ ${item.error.message}` : ""}`, - }); - else if (item.type === "agent_message") log.info(label, { line: `● ${item.text}` }); - } else if (event.type === "turn.failed") { - maybeRotateGateway({ reason: event.error.message }); - throw new Error(event.error.message); - } else if (event.type === "error") throw new Error(event.message); + const abort = new AbortController(); + const timer = setTimeout(() => { + abort.abort(); + }, timeoutMs); + try { + const { events } = await thread.runStreamed(prompt, { signal: abort.signal }); + for await (const event of events) { + if (event.type === "item.completed") { + const { item } = event; + if (item.type === "command_execution") log.info(label, { line: `$ ${item.command}` }); + else if (item.type === "mcp_tool_call") + log.info(label, { + line: `⚙ ${item.tool} ${JSON.stringify(item.arguments)}${item.error ? ` ✗ ${item.error.message}` : ""}`, + }); + else if (item.type === "agent_message") log.info(label, { line: `● ${item.text}` }); + } else if (event.type === "turn.failed") { + maybeRotateGateway({ reason: event.error.message }); + throw new Error(event.error.message); + } else if (event.type === "error") throw new Error(event.message); + } + } finally { + clearTimeout(timer); } } }