From 5ab1a2733c0fde0eb6879180b026e06e9f8e9725 Mon Sep 17 00:00:00 2001 From: gavrielc Date: Fri, 1 May 2026 00:55:46 +0300 Subject: [PATCH] review: catch follow-up poll errors + re-check done before push MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two fixes on top of the follow-up pre-task-script work: 1. The void async IIFE inside the interval handler had no catch, so a throw from the dynamic import or applyPreTaskScripts escaped as an unhandled rejection — terminating the container. The initial-batch path is wrapped by processQuery's outer try/catch; the follow-up path needs its own. Now logs the error and lets the next tick retry. 2. Re-check `done` immediately before query.push. The flag can flip true while applyPreTaskScripts is awaited (outer stream finishes during the script execution); without the re-check we'd push into a closed query. Claimed messages get released by the host's processing-claim sweep — same recovery posture as the rest of the poller. Co-Authored-By: Michael Zazon Co-Authored-By: Claude Opus 4.7 (1M context) --- container/agent-runner/src/poll-loop.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/container/agent-runner/src/poll-loop.ts b/container/agent-runner/src/poll-loop.ts index 589b80b..986489f 100644 --- a/container/agent-runner/src/poll-loop.ts +++ b/container/agent-runner/src/poll-loop.ts @@ -302,12 +302,23 @@ async function processQuery( // MODULE-HOOK:scheduling-pre-task-followup:end if (keep.length === 0) return; + // Re-check done — the outer query may have finished while the script + // was awaited. Pushing into a closed stream is wasted work; the + // claimed messages get released by the host's processing-claim sweep. + if (done) return; const keptIds = keep.map((m) => m.id); const prompt = formatMessages(keep); log(`Pushing ${keep.length} follow-up message(s) into active query`); query.push(prompt); markCompleted(keptIds); + } catch (err) { + // Without this catch the rejection escapes the void IIFE and Node + // terminates the container on unhandled-rejection. The initial-batch + // path is wrapped by processQuery's outer try/catch; the follow-up + // path is not, so it needs its own. + const errMsg = err instanceof Error ? err.message : String(err); + log(`Follow-up poll error: ${errMsg}`); } finally { pollInFlight = false; }