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
Series:ROADMAP — filed from the 2026-10-27 whole-project assessment · Backlog task: M14-T01 · Priority: P0 · Effort: small
Problem
Agent.run has handlers only for PersistenceError, TimeoutError and Exception. Since Python 3.8 asyncio.CancelledError derives from BaseException, which none of those catch. When an HTTP client disconnects mid-run, Starlette cancels the request task and the cancellation propagates straight through run:
_finalize_and_return never runs → the run row stays status='running' in PostgreSQL forever (zombie row)
session history is never saved and never gets the _repair_trailing_tool_calls fix applied
the in-flight step is invisible in telemetry
The asymmetry is stark: a server-side timeout produces a truthful terminal state (TIMED_OUT), a client-side disconnect produces an orphan. Grep confirms zero references to CancelledError, asyncio.shield or request.is_disconnected in the codebase.
Evidence
agentkit/core/agent.py:310–526 — the try block and its three except clauses
agentkit/core/agent.py:178 — create_run happens before any finally could persist a terminal state
Suggested fix / acceptance criteria
Agent.run catches asyncio.CancelledError, persists a terminal run status (canceled) inside asyncio.shield (so a dying request cannot abort mid-write), then re-raises
_repair_trailing_tool_calls runs on the cancellation path so the saved history stays provider-valid
Unit test simulates cancellation mid-tool and asserts a terminal status row and a valid history (no running rows)
Suggested branch / commit
fix/cancelled-run-terminal-state · fix(core): persist terminal state on client-disconnect cancellation
Activity