Repository navigation
Description
What happened
A Cursor thread (claude-opus-5-5 via the Cursor harness) stopped mid-run. The user believes the Cursor account ran out of tokens. Shortly after, the whole T3 server process exited with this error on stderr:
ConnectError: [aborted] write after end
rawMessage: 'write after end', code: 10,
cause: Error [ERR_STREAM_WRITE_AFTER_END]: write after end
Diagnosis
The crash happens in the startup recovery path, when T3 cancels an abandoned Cursor run.
- The server restarted (16:05:33 UTC) and resumed the interrupted Cursor turn on thread
1423ffbd…with "Continue where you left off". agent.sendfailed withalready has active run. The recovery inCursorAgentSdk.ts(isActiveRunConflict→Agent.listRuns(..., { limit: 1 })) found the orphaned run. It was the agent's only run, solimit: 1happened to return it. Recovery then calledAgent.cancelRun(activeRun.id, runOptions)(packages/provider-cursor/src/server/CursorAgentSdk.ts:497).- Provider log:
run.cancel run-f74057a9…at 16:05:37.559Z. The retriedrun.startthen succeeded (run.started run-6754ccbc…at 16:05:41.662Z). The server trace stops at 16:05:41.70Z, and the process is gone. - Inside
@cursor/sdk(dist/cjs/126.js), the cancel writes to a Connect stream that has already ended. That promise is not awaited by anything T3 holds, so it becomes an unhandled rejection. - The guard in
packages/provider-cursor/src/server/CursorSdk.ts(onUnhandledRejection) only passesisCursorShellSpawnFailureandisCursorClosedStreamWrite(name === "WriteIterableClosedError"). This error is aConnectErrorwith code 10 and causeERR_STREAM_WRITE_AFTER_END, so the guard falls through toconsole.error(reason); process.exit(1).
This looks like the same SDK pattern the existing WriteIterableClosedError guard covers (a final write to a closed stream from an unawaited promise), arriving through a different error class.
Interaction with #24498 / PR #24540: today the limit: 1 lookup usually misses the stuck run, so cancelRun rarely executes. That is why most users see #24498 (thread stuck) and not this crash. PR #24540 makes recovery find the stuck run reliably, so this cancelRun path, and this crash, will run far more often once it lands. The two should probably be fixed together.
The server also exited earlier, at 16:03:59 UTC, about 1 s after a new run started on the same thread. No stderr is available for that exit, so I can't confirm it was the same error.
Steps to reproduce
Seen once on this machine. These are the steps that led to it; not yet confirmed as a deterministic repro:
- Start a Cursor-provider thread and send a turn that runs for a while.
- While the run is in progress, kill the T3 server so the run is orphaned in the Cursor SDK local store. Make sure this is the agent's first run, so
listRuns({ limit: 1 })returns it. - Restart the server. Startup recovery resumes the turn, hits
already has active run, finds the run, and callsAgent.cancelRun. - A few seconds later the server exits with
ConnectError: [aborted] write after end.
Version
0.1.6-nightly.20261030.3917 (server, t3 serve --tailscale-serve), @cursor/sdk 1.0.38. Still present on main as of 2026-10-31: the CursorSdk.ts guard is unchanged.
Environment
Raspberry Pi 5, Linux 6.18.50 arm64, Node v26.12.1, provider cursor (model claude-opus-5-5)
Evidence
# stderr at crash (paths redacted)
ConnectError: [aborted] write after end
at ConnectError.from (~/.t3/runtime/versions/0.1.6-nightly.20261030.3917/node_modules/@connectrpc/connect/dist/cjs/connect-error.js:74:20)
at i (.../node_modules/@cursor/sdk/dist/cjs/126.js:1:749)
at .../node_modules/@cursor/sdk/dist/cjs/126.js:1:13906
at process.processTicksAndRejections (node:internal/process/task_queues:89:21) {
rawMessage: 'write after end',
code: 10,
metadata: Headers {},
details: [],
cause: Error [ERR_STREAM_WRITE_AFTER_END]: write after end
at _write (node:internal/streams/writable:491:11)
at Writable.write (node:internal/streams/writable:512:10)
at .../node_modules/@cursor/sdk/dist/cjs/126.js:1:13835
at process.processTicksAndRejections (node:internal/process/task_queues:104:5) {
code: 'ERR_STREAM_WRITE_AFTER_END'
}
}
# provider event log, thread 1423ffbd… (UTC)
16:03:54.338Z outgoing run.start
16:03:58.998Z incoming run.started run-f74057a9…
(server exits ~16:03:59, restarts 16:05:33)
16:05:35.497Z outgoing agent.open
16:05:37.557Z outgoing run.start "Continue where you left off."
16:05:37.559Z outgoing run.cancel run-f74057a9… <- abandoned-run recovery
16:05:41.662Z incoming run.started run-6754ccbc…
(last trace span 16:05:41.70Z, then process exits)Related issues
#24498 / PR #24540: same recovery path. That issue covers the run lookup missing the stuck run, which leaves the thread stuck. This issue covers what happens when the lookup does find it: cancelRun crashes the whole server. Not a duplicate.
Fix applied or workaround
None applied. Restarting the server brings it back, but then the affected threads hit #24498 and stay stuck. Starting a new thread works.
Filed by
Claude Code (claude-opus-5-5) via t3 triage
Activity