Reproduced on current main at 8acbefefc5: Linux web dev server, Claude Opus 5.5 running a foreground sleep 90, then provider.switch to a second Claude instance. The turn immediately failed with the reported unknown stream error. Runtime transcript. Existing draft fix #22754 is being rechecked under the revised triage brief.
Switching a thread's provider instance during a running turn kills the turn with "provider event stream closed unexpectedly"#22748
What happened
I run two accounts per harness as separate provider instances (personal claudeAgent / codex, work claude-work / a managed Codex instance). Turns randomly fail with "The provider event stream closed unexpectedly", on whichever provider happened to be running. It looked like a provider or account problem.
Diagnosis
The failures are caused by T3 itself: switching a thread's provider instance while the thread has a run in progress tears down the live session mid-turn. The turn then ends as a generic unknown failure instead of being refused, deferred, or reported clearly.
In my case an orchestrator agent was moving a set of worker threads from claude-work back to claudeAgent with t3_thread_configure (one call per thread). Several of those workers were mid-turn, and the orchestrator also switched its own thread. Every running thread it touched failed within ~150 ms.
Code path at v0.1.2-nightly.20261021.3457 (unchanged on main as of today):
t3_thread_configure(apps/server/src/mcp/toolkits/thread/handlers.ts:182) dispatchesprovider.switchwhen the instance changes (modelSelectionCommandType).ProviderSwitchService(apps/server/src/orchestration-v2/ProviderSwitchService.ts:180) puts every live session from another instance inreleaseProviderSessionIdsfor acreate_with_handofftransition. It does not check whether a run is active.- The decider (
apps/server/src/orchestration-v2/Orchestrator.ts:3640-3712) emitsprovider-session.detachedand queues aprovider-session.detacheffect with detail"Provider or model selection changed.". There is no active-run guard, unlikethread.settle, which refuses with "is still running. Stop it before settling." (Orchestrator.ts:2835-2850). ProviderSessionManager.detachcloses the session (query.closegoes to the Claude SDK while the model is still producing tool calls). The run's subscribers get a stream error, and the turn is recorded asclass: "unknown"/ "The provider event stream closed unexpectedly. Retry the turn; if it keeps failing, check the provider and server logs."
The detail string that explains the failure exists in the outbox, but the user never sees it.
Expected, any of:
- refuse
provider.switch/thread.model-selection.seton a thread with an active run (like settle), or - defer the switch until the current run ends, or
- at minimum, interrupt the run cleanly and report "Provider was switched while this turn was running" rather than a generic stream failure.
Steps to reproduce
- Configure two provider instances of the same driver (e.g.
claudeAgentand a secondclaudeAgentinstance with anotherhomePath). - Start a long turn on a thread using instance A (e.g. ask it to run several tool calls).
- While it runs, switch the thread to instance B, either from another agent via the T3 MCP tool
t3_thread_configurewithmodelSelection.instanceId = B, or by having the running agent call it on its own thread. - The running turn fails immediately with "The provider event stream closed unexpectedly".
Seen 8 out of 8 times in my logs: every switch issued against a running thread failed its turn. A switch in the same batch against an idle thread (07:42:15) caused no error.
Version
0.1.2-nightly.20261021.3457 (desktop app, nightly)
Environment
macOS 26 (Darwin 25.6.0) arm64, Node 26.8.2, Claude Code 2.1.306, codex-cli 0.163.0
Evidence
| 1 | # effect outbox: every "stream closed" failure has a matching detach from an MCP provider.switch |
| 2 | provider-session.detach | Provider or model selection changed. | mcp:1bffb82f-… | 2026-10-21T07:40:19.508Z |
| 3 | provider-session.detach | Provider or model selection changed. | mcp:f167cd1a-… | 2026-10-21T07:42:12.129Z |
| 4 | provider-session.detach | Provider or model selection changed. | mcp:85ebf963-… | 2026-10-21T07:42:12.922Z |
| 5 | provider-session.detach | Provider or model selection changed. | mcp:5db90f4e-… | 2026-10-21T07:42:14.059Z |
| 6 | provider-session.detach | Provider or model selection changed. | mcp:3bbdd80f-… | 2026-10-21T07:42:16.336Z |
| 7 | provider-session.detach | Provider or model selection changed. | mcp:378614ad-… | 2026-10-21T07:42:17.531Z |
| 8 | provider-session.detach | Provider or model selection changed. | mcp:c044a900-… | 2026-10-21T07:44:04.767Z # orchestrator switching its own thread |
| 9 | provider-session.detach | Provider or model selection changed. | mcp:541eab28-… | 2026-10-21T07:51:57.508Z |
| 10 | |
| 11 | # server trace, same instant |
| 12 | 07:42:12 orchestrationV2.dispatch.once {"command_type":"provider.switch","thread_id":"mcp:670e053c-…"} |
| 13 | 07:42:12 ProviderSessionManager.detach -> ProviderSessionManager.closeScopeWithin |
| 14 | |
| 15 | # provider event log for that thread: model mid-turn, then T3 closes it |
| 16 | [07:42:08.391Z] incoming system/status "requesting" |
| 17 | [07:42:12.144Z] outgoing {"type":"query.close"} |
| 18 | |
| 19 | # resulting turn item |
| 20 | {"title":"…","failure":{"class":"unknown","message":"The provider event stream closed unexpectedly. Retry the turn; if it keeps failing, check the provider and server logs."}} |
Related issues
#15135 is the same failure shape for thread.archive (self-archive detaches the live session without an active-run guard). This one is provider.switch / thread.model-selection.set, a different command and code branch, so a fix for archive alone won't cover it. #21369 covers the poor reporting after this error in general.
Fix applied or workaround
None applied on the machine. Workaround: only switch a thread's provider instance when it has no active run, and never let an agent switch its own thread mid-turn.
Filed by
Claude Code (claude-opus-5-5) via t3 triage
Fix #22754 now refuses disruptive provider switches while a turn is active. Same-driver Claude runtime verification preserved the running turn; idle switches and next-turn selections remain available. On merged head 896c0e10d3, 109 focused tests, server typecheck and targeted lint passed. Both independent reviews found no blocking defects. Evidence and limits.