[Bug]: Cursor thread never recovers after a restart during a run ("already has active run")#24498
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/server — packages/provider-cursor
Steps to reproduce
- Start a Cursor turn that runs for a while, e.g. ask it to run
sleep 90. - While it is running, stop and restart the T3 server process.
- The server comes back and resumes the parked turn on its own ("T3 Code restarted and resumed this turn").
- Every send to that thread now fails with "The provider could not start this turn", and retrying never helps.
Expected behavior The resumed turn recovers: the abandoned run is cancelled and the turn continues. At worst it fails once and a retry works.
Actual behavior Every attempt fails with:
| 1 | CursorAgentSdkRunnerError: Cursor Agent SDK run.start failed. |
| 2 | [cause]: UnknownAgentError: Agent agent-… already has active run |
T3 has recovery for exactly this (isActiveRunConflict in CursorAgentSdk.ts), but it reads one page of runs:
| 1 | const latestRun = yield* Effect.tryPromise({ |
| 2 | try: () => Agent.listRuns(agent.agentId, { ...runOptions, limit: 1 }), |
@cursor/sdk returns runs oldest first, so limit: 1 is the first turn of the conversation — finished — not the run still marked running. find(status === "running") returns undefined, the original error is rethrown, and nothing is cancelled. The provider log shows run.start with no run.cancel and no later events.
The SDK's local store blocks the agent: agents.status = RUNNING, agents.active_run_id = run-…, and that run row is still RUNNING. The SDK can expire that with local: { force: true }, but the resume path never sends it, and nothing else clears it. Retrying therefore cannot recover the thread.
Impact Blocks work completely for the affected thread. Restarting T3 again does not help, because the stale run survives in the store.
Version or commit
main @ d53873b6a. Also present on v0.1.2 and v0.1.3-nightly.20261024.3617. @cursor/sdk 1.0.35.
Environment
Linux x64 and desktop app, Node 22, provider cursor (Auto and grok-4.7). Reproduced twice on the same host: once by accident during a release restart, once deliberately with the steps above.
Workaround Cancel the orphan through the SDK, then send again:
| 1 | require("@cursor/sdk").Agent.cancelRun("<run-id>", { runtime: "local", cwd: "<thread cwd>" }) |
The partial output of the killed run is still in its checkpoint. Restarting T3 is not a workaround.
I am sending a small fix that references this issue. It pages the run list and cancels the abandoned run, which keeps the recovery's existing shape; local: { force: true } would be shorter, so say if you would rather have that one.