You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug + Tested Fix] Scheduler failure leaves dangling tool/call without tool/result and discards in-flight results (Fix included in branch)
#8782
When parallel tool execution encounters a scheduler or environmental failure (such as Win32 sandbox permission denial grantWrite or uncaught runtime exceptions), two critical issues occur in agent-loop:
Discarded In-Flight Results: In packages/core/agent-loop/src/tool-calls.ts, when a tool throws during prepare(), the catch handler drains running calls via await Promise.allSettled(inFlight.values()), but rethrows immediately without calling commitReady(). Sibling tools that completed successfully during the drain have their real outputs in slots discarded and falsely classified as failed.
Session Poisoning & Empty UI: In packages/core/agent-loop/src/agent.ts, when an exception escapes step(), toolRecovery can fail to capture events if scoped listeners are filtered by Cordis scope carrier rules, leaving the turn closed with turn/end { error } while prior tool/calls remain unanswered. This permanently poisons the session (HTTP 400 insufficient tool messages following tool_calls message on future turns) and causes the Web GUI to render no recorded result.
In runGroup's catch handler, call await commitReady().catch(() => {}) after Promise.allSettled(inFlight.values()) to commit all contiguous settled slots that completed successfully during the drain.
In step()'s error recovery, fallback to scanning this.session.snapshotEvents() if toolRecovery.results() is empty, guaranteeing that any pending call is observed regardless of scope carrier filtering.
In turn()'s finally block before writing turn/end { error }, introduce a final safety gate to ensure all unanswered tool/calls receive conservative recovery results, guaranteeing that no session log ever closes with dangling tool calls.
Added unit test it('preserves genuine in-flight tool results when a sibling fails during prepare') verifying that when sibling C fails during prepare(), sibling B (which was in-flight and succeeded during drain) preserves its genuine result (isError: false, content: "done-2").
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Summary & Problem
When parallel tool execution encounters a scheduler or environmental failure (such as Win32 sandbox permission denial
grantWriteor uncaught runtime exceptions), two critical issues occur inagent-loop:packages/core/agent-loop/src/tool-calls.ts, when a tool throws duringprepare(), the catch handler drains running calls viaawait Promise.allSettled(inFlight.values()), but rethrows immediately without callingcommitReady(). Sibling tools that completed successfully during the drain have their real outputs inslotsdiscarded and falsely classified as failed.packages/core/agent-loop/src/agent.ts, when an exception escapesstep(),toolRecoverycan fail to capture events if scoped listeners are filtered by Cordis scope carrier rules, leaving the turn closed withturn/end { error }while priortool/calls remain unanswered. This permanently poisons the session (HTTP 400insufficient tool messages following tool_calls messageon future turns) and causes the Web GUI to renderno recorded result.Relates to #4549, #2900, #1959.
Tested Solution & Pull Branch
I have implemented a complete, minimal, and fully-tested fix in my fork repository:
fix/tool-scheduler-drain-and-wire-containment08940dd65Proposed Changes
packages/core/agent-loop/src/tool-calls.ts):runGroup's catch handler, callawait commitReady().catch(() => {})afterPromise.allSettled(inFlight.values())to commit all contiguous settled slots that completed successfully during the drain.packages/core/agent-loop/src/agent.ts):step()'s error recovery, fallback to scanningthis.session.snapshotEvents()iftoolRecovery.results()is empty, guaranteeing that any pending call is observed regardless of scope carrier filtering.turn()'s finally block before writingturn/end { error }, introduce a final safety gate to ensure all unansweredtool/calls receive conservative recovery results, guaranteeing that no session log ever closes with dangling tool calls.packages/core/agent-loop/tests/tool-calls.spec.ts):it('preserves genuine in-flight tool results when a sibling fails during prepare')verifying that when sibling C fails duringprepare(), sibling B (which was in-flight and succeeded during drain) preserves its genuine result (isError: false,content: "done-2").Git Diff
Please review this patch and consider cherry-picking it into upstream master.
All reactions