Repository navigation
Description
What happened
I moved from the built-in Claude instance to a custom instance (claude_proxy, driver claudeAgent, its own homePath) and disabled the built-in one. After that, every thread on the desktop app showed "Context handoff pending · Claude Opus 5.5 → Claude Opus 5.5" below the last turn. All turns still ran on claude_proxy, so no handoff was ever going to happen. The banner was just always there.
Picking Claude (proxy) explicitly in the model picker once cleared it, and new threads stay clear after that.
Diagnosis
The composer and the handoff banner resolve the selected provider instance in different ways:
- The composer, picker and send path use
resolveComposerProviderSelection(apps/web/src/components/ChatView.logic.ts:570). It only accepts a candidate instance that isentry.enabled && entry.isAvailable(line 603). A draft selection pointing at the disabledclaudeAgentis skipped, and the thread's instance (claude_proxy) wins. Turns run onclaude_proxy. - The banner uses
resolvePendingHandoff(packages/client-runtime/src/handoff.ts:23). It gets the raw draft selection:composerSelectedModelSelection ?? activeThread?.modelSelection(apps/web/src/components/ChatView.tsx:7566, fromdraft.modelSelectionByProvider[draft.activeProvider]at line 1914). It compares that with the latest run'smodelSelectionand doesn't check whether the target instance is enabled, or whether the user picked the selection rather than having it seeded.from.instanceId(claude_proxy) ≠selection.instanceId(claudeAgent), and the two instances don't share a continuation group, soneedsHandoffis true.
The stale draft selection comes from the composer's persisted sticky state (stickyModelSelectionByProvider / stickyActiveProvider in apps/web/src/composerDraftStore.ts, applied by applyStickyState). It still pointed at claudeAgent from before the switch, so every new thread's draft was seeded with it. Picking the proxy instance explicitly overwrote the sticky state, which matches the workaround.
Mobile probably has the same bug: apps/mobile/src/state/use-thread-composer-state.ts:340 also calls resolvePendingHandoff. I haven't checked mobile's input selection.
Possible fix: pass resolvePendingHandoff the resolved composer selection (the instance from resolveComposerProviderSelection) rather than the raw draft, or have it ignore targets that aren't enabled and available. It would also make sense to drop or re-seed sticky selections that point at a disabled instance.
Steps to reproduce
- With the built-in Claude instance enabled, send a message in any thread from the desktop or web client so the composer's sticky selection is
claudeAgent. - In Settings → Providers, add a custom Claude instance (e.g.
claude_proxy, driverclaudeAgent, separateCLAUDE_CONFIG_DIR). Make it the default, then disable the built-in Claude instance. - Start a new thread. The picker shows the custom instance. Send a message. The turn runs on the custom instance.
- After the turn finishes, "Context handoff pending · Claude Opus 5.5 → Claude Opus 5.5" appears and stays, including on later threads.
- Explicitly pick the custom instance in the model picker. The banner disappears, and new threads no longer show it.
Version
0.0.46-nightly.20261015.3158 (server and desktop). The code paths above are unchanged on main @ 42da166.
Environment
Server: Linux, headless t3 serve, reached over Tailscale. Client: T3 Code (Nightly) desktop on Windows. Instances: claude_proxy (driver claudeAgent, Claude Code 2.1.300) and codex_proxy (driver codex, codex-cli 0.162.1), with built-in claudeAgent and codex disabled.
Evidence
# Thread whose desktop view showed the banner (server statev2.sqlite)
orchestration_v2_projection_threads.provider_instance_id = claude_proxy
payload.modelSelection = {"instanceId":"claude_proxy","model":"claude-opus-5-5"}
orchestration_v2_projection_runs (ordinal 1..3) = claude_proxy, modelSelection.instanceId = claude_proxy
orchestration_v2_projection_provider_sessions = claude_proxy (driver claudeAgent), status ready
orchestration_v2_projection_context_handoffs (this thread) = 0 rows
MCP t3_thread_transfers = {"transfers": []}
# settings.json
"defaultModelSelection": { "instanceId": "claude_proxy", "model": "claude-opus-5-5" }
"claudeAgent": { "driver": "claudeAgent", "enabled": false }
# Manual check on the desktop client
1. Explicitly select "Claude (proxy)" in this thread's picker -> banner disappears
2. Start a new thread -> no bannerRelated issues
#15476: a real handoff between two instances of one provider shows "Claude Opus 5.5 → Claude Opus 5.5" because the endpoints lack the instance badge. That's why this phantom banner looks like a no-op, but the cause is different (rendering vs. selection resolution). #17506: hard to tell which provider instance is active in the composer. Related UX, not the same bug.
Fix applied or workaround
Explicitly select the intended instance in the model picker once. This overwrites the sticky selection. Nothing was changed on the server.
Filed by
claude (claude-opus-5-5) in a T3 Code thread, not via t3 triage. The diagnosis was checked against server state and main @ 42da166, and the workaround was confirmed manually by the user.
Activity
juliusmarminge commentedon Oct 15, 2026
Note
Grok responding on behalf of Julius.
Thanks for the detailed write-up. This is the same bug that open PR #19523 fixes: the pending-handoff preview now only considers a selection the next send can actually use (provider enabled and available, same filter as the composer), on both web/desktop and mobile. I've linked this issue to that PR so it closes when it merges. Until then, explicitly picking the custom instance in the model picker clears it, as you found.
arnaud-dezandee commentedon Oct 17, 2026
Same bug here on a remote environment: I started a new thread on an empty project with Claude Opus 5.5, and it immediately shows "Context handoff pending Claude Opus 5.5 → xiaomi-token-plan-ams/mimo-v2.6-pro" even though I never picked that model.
The OpenCode provider is disabled on that remote host, so it looks like the sticky composer selection from another environment leaks into the remote one and the pending-handoff preview doesn't check whether the target is enabled there.
SwiggitySwerve commentedon Oct 18, 2026
#21139 / #21140 cover the cause of this symptom. On my install the stale draft came from
defaultModelSelectionpointing at a disabled instance, not from sticky state. Because the selection was seeded rather than picked, the send would switch the thread to that provider as soon as it's enabled again. #19523 hides the banner while the target is unavailable. #21140 stops a non-explicit seed from overriding a started thread at all, so the two are complementary.juliusmarminge commentedon Oct 20, 2026
Note
Grok responding on behalf of Julius.
Fixed by #19523, which just merged: the "Context handoff pending" preview now only shows for a provider the next send would actually use, on web/desktop and mobile. The related case where a seeded model overrides a started thread is tracked separately in #21139 (fix in #21140). If you still see a phantom handoff on a build that includes #19523, including on a remote environment, comment here and we will take another look.