Repository navigation
Description
What happened
When an agent calls t3_thread_organize with action: "archive" and no threadId (its own thread) during its turn, the archive succeeds. The run then ends as failed with "The provider event stream closed unexpectedly", even though every earlier step in that turn succeeded. Expected: either the archive waits until the turn ends, or self-archive is refused with a clear error. Reproduces 4 out of 4 runs, desktop app, macOS arm64.
Diagnosis
thread.archive has no active-run guard, and it tears down the caller's live provider session.
t3_thread_organize(apps/server/src/mcp/toolkits/thread/handlers.ts:275) dispatchesthread.archivefor the caller's thread.assertLiveCaller(mcp/threadAccess.ts:40) requires the caller to have an active run, so a self-archive always targets a thread in the middle of a turn.- The decider only rejects threads that are already archived (
orchestration-v2/Orchestrator.ts:2264). Archive cancelsqueuedruns only (:2964) and leaves therunningrun alone. It then detaches every provider session withdetail: "Thread archived."andrevokeMcpCredential: true(:3033-3106). The comment at:3026says this is safe for settle because settle rejects active runs. Archive has no such check. - For a single-thread session (Claude here),
ProviderSessionManager.detachreleases the entry (:1871).releaseEntrythen callsfailSubscribers(:512), which pushesProviderAdapterEventStreamError("Thread archived.")into the run's event stream. RunExecutionService(:1267-1307) still sees the run asrunning, so it writes a failed terminal.ProviderFailure.ts:47maps that to the generic "stream closed unexpectedly" text, and "Thread archived." is never shown.
For comparison: settle rejects active runs (Orchestrator.ts:2352), delete cancels them before detaching (ThreadDeletion.ts:70), and the web UI disables Archive while a thread is running (threadActionMenu.logic.ts:51). That UI code's comment says the server rejects the action, which v2 no longer does. Codex and OpenCode2 sessions take the interruptTurn path in detach, so they would likely end as interrupted instead (not tested). On main (e8545b2), Orchestrator.ts and the MCP toolkit are unchanged since this tag, and releaseEntry still fails subscribers.
Possible fixes: reject thread.archive while a run is active, as settle does, so the agent gets a clear error. Or defer a self-archive until the turn ends.
Steps to reproduce
- In the desktop app, start a thread on Claude.
- Ask the agent to call
t3_thread_organizewithaction: "archive"and nothreadId, then reply "done". - The thread is archived within about 10 ms of the tool call starting, and the run fails with "The provider event stream closed unexpectedly". The tool call item stays
running.
Version
0.0.46-nightly.20261003.2623 (fed41fa)
Environment
macOS 27.0 (26A428) arm64, Node v26.8.2, Claude Code 2.1.288, desktop app with local server
Evidence
# statev2.sqlite projections, 2026-10-03 (UTC). These are the only failed runs in the database.
thread tool started archived_at run failed last tool call
cd17f2c6 09:29:55.154 09:29:55.165 09:29:55.172 t3_thread_organize {"action":"archive"} [running]
feb9e7ee 09:35:33.715 09:35:33.722 09:35:33.729 same
5f2febba 09:55:35.674 09:55:35.683 09:55:35.689 same
3d61eeab 09:55:36.278 09:55:36.289 09:55:36.294 same
94e4b217 09:58:11.197 09:58:11.200 09:58:11.204 same
67d809c6 09:58:17.022 09:58:17.026 09:58:17.029 same
error turn item: {"class":"unknown","message":"The provider event stream closed unexpectedly. Retry the turn; if it keeps failing, check the provider and server logs.","code":null,"retryable":null}Related issues
#14203 (closed) asked for archive to stop the provider session, which v2 now does, but nothing handles a running turn. #14907 (open PR) covers archived threads starting work after setup, which is a different path. I found no existing issue for archiving a thread with a running turn.
Fix applied or workaround
Nothing changed on the machine. Workaround: archive from another thread, or manually after the turn ends. Settle is not a workaround from inside the turn, because it rejects active runs.
Filed by
Claude Code 2.1.288 (claude-opus-5-5) via t3 triage
Activity
juliusmarminge commentedon Oct 3, 2026
Note
Grok responding on behalf of Julius.
Triage
Thanks @0pilatos0 for the detailed diagnosis and the database evidence! I confirmed this on current
main(263097a8). The nightly you reported on (fed41fa8) is an ancestor of that commit, and nothing since has changed this path.What happens
thread.archiveonly refuses a thread that's already archived. It cancelsqueuedruns, then detaches every live provider session with detailThread archived.andrevokeMcpCredential: true. A run that ispreparing,starting, orrunningis left in place. By comparison, settle rejects active work before it detaches, and delete cancels active runs first. Archive does neither.The clients already treat archiving a live turn as off-limits.
threadRuntimeCanArchiveallows dropping queued work but not detaching a provider that's preparing, starting, or running, and the web and mobile archive actions are disabled in that state. The menu comment saying the server rejects the action is out of date.A self-archive through
t3_thread_organize(withaction: "archive"and nothreadId) always hits this.readWritableThreadrequires the caller to own an active run, then dispatchesthread.archiveon that same thread. For a single-thread session like Claude, detach releases the session, andfailSubscriberspushes aProviderAdapterEventStreamErrorwith causeThread archived..RunExecutionServicestill sees the run asrunning, so it writes a failed terminal.ProviderFailurethen swaps the cause for the generic "provider event stream closed unexpectedly" text, which matches all six rows you posted. The tool item staysrunningbecause the stream dies before the tool result comes back.Shared multi-thread sessions (Codex, OpenCode) interrupt or unload rather than going through
releaseEntry, so they may end up interrupted instead of failed. That's still untested.Related
#14203 (closed) was about stopping an idle provider process on archive. That teardown is now what kills the live turn. Open PR #14907 is a different path: setup finishing and starting work after the thread is already archived.
Likely fix area
One option is to make
thread.archivereject a thread with a preparing, starting, or running run, the way settle does. The agent would then get a clear error and the turn could finish, while queued work can still be cancelled. Deferring a self-archive until the turn ends would be a new workflow. A maintainer will decide on the fix direction.Your workaround holds: archive the thread only after its turn is idle, either from the UI or from another thread. Archiving it from anywhere while the turn is still running goes through the same server path.
juliusmarminge commentedon Oct 22, 2026
Reproduced on current main 89fe509232, Linux x86_64, with
vp run devin an isolated worktree using copied development state. In the real web client, asked Claude Opus 5.5 to callt3_thread_organizewith{"action":"archive"}and nothreadId, then replydone.Read-only projection evidence (UTC, 2026-10-22):
No matching open issue or PR found in the last 90 days. #22748 has a different trigger (switching providers); #14907 addresses workspace preparation, not an already running turn.
Leaving open for maintainer decision: refusing or deferring an agent's self-archive changes the MCP tool's allowed behavior, which is outside the automated triage fix scope. No implementation or PR created. macOS/Electron was not verified here.
juliusmarminge commentedon Oct 22, 2026
Reproduced on current main defb7e6e6b, Linux x86_64, using an isolated
vp run devserver and the real web client with Claude Opus 5.5. Asked it to callt3_thread_organizewith{"action":"archive"}and nothreadId, then replydone. At 03:16:58.046 UTC the thread archived; at 03:16:58.079 the run failed with “The provider event stream closed unexpectedly.” The tool item remained running. No matching duplicate found in the open issues from the last 90 days. macOS/Electron has not been verified here.bompus commentedon Oct 31, 2026
Adding a real caller for the "defer" option mentioned in the issue. We close agent sessions by having the finished agent hand its leftover items to a coordinator thread and then archive its own thread as its last step (
t3_thread_organizewithaction: "archive", nothreadId). Observed on0.1.6-nightly.20261030.3940(Linux): the thread archived, the Claude process ended two seconds later, and the run showed "The provider event stream closed unexpectedly".Refusing the archive while a run is active (#23451) fixes the misleading error, and a clear refusal is much better than today's behaviour. I'm noting that deferring the archive until the run reaches a terminal state would keep this closing step working from inside the turn, whereas a refusal means the agent has to end its turn and wait for someone else to archive the thread. Happy with either direction; this is only a use case for the deferral.
bompus commentedon Nov 1, 2026
More data from a long-lived database (Linux, nightlies 0.1.4-0.1.6, statev2.sqlite read-only). Of 734 archived threads with runs, 97 had their last run end within 2 s of the archive. None of those 97 ended completed: 50 failed, 46 interrupted, 1 cancelled. By provider the split is consistent with the diagnosis above: Claude-based instances and muse end failed, codex and opencode end interrupted. Of the other 637 archived threads, 600 end completed. In the failed ones, 22 of 50 had a completed assistant message in the same run before the failure and 7 have the t3_thread_organize call stuck running. Measured: the counts and the status split. Not checked: which consumers read the last run's status, and the exact adapter line that writes the failure (the mechanism above is from the existing diagnosis). This is why a refusal (#23451) fixes the misleading error, while a deferral would keep self-archive working as a closing step.