Repository navigation
[Bug]: Claude refuses every message as a "model or setting change" when a thread moves between mobile and web while background work runs #15471
Description
Activity
juliusmarminge commented
on Oct 4, 2026 MemberMore actionsNote
Grok responding on behalf of Julius.
Triage
Thanks @vitalyiegorov for the run records and the root-cause write-up; they made this quick to confirm. I checked it on
8fb068c498(the nightly you reported against), and the code matches your table.What I found
- Web and desktop send Normal as an explicit
fastMode: false.withImplicitFastModeDefaultinapps/web/src/components/chat/composerProviderState.tsxadds that option for every model with a fast-mode descriptor. Mobile only keeps the options that were actually selected, so a Claude thread started there has nofastModeoption. compileClaudeModelSelection(apps/server/src/claudeModelOptions.ts) keeps them apart: an omitted value stays out ofsettings, whilefalsebecomes{ fastMode: false }.queryIdentityincludes that object, so the two sends don't match.openQueryinClaudeAdapterV2.tsonly reuses the live process when the identity matches. While that process has background work and isn't stopping, the mismatch raisesClaudeBackgroundWorkBlocksQueryReplacementError, which is the message you saw. Opus 5.5 exposesfastModein the bundled catalog, so this model hits it.- Server continuations skip the check (
isClaudeProviderContinuationTurn), which is why runs 15 and 16 completed with no options. - After the shell exits, the next web send still has a different identity, so the process is replaced and resumed. That explains the Opus 5.5 to Opus 5.5 handoff without any real model change.
- Stop failing afterwards is tracked in [Bug]: Stop can't end background work after a run fails before its provider starts ("Run … is not interruptible") #15472: this refusal fails the run before a provider turn exists, so interrupt rejects that run.
Likely fix area
Some options:
- Normalize an unset
fastModetofalseincompileClaudeModelSelectionfor models that expose fast mode, so omitted and explicit Normal produce the same identity. A real switch to Fast would still change the identity, so the fix(server): Claude model changes no longer kill running background agents #14726 guard would keep blocking it while background work runs. That's the approach in your open PR fix(claude): a thread with background work no longer refuses messages sent from another client #15473. - Alternatively, normalize on the clients so mobile and web send the same option set, though a server-side fix would also cover older clients and continuations.
A maintainer will decide on the fix direction.
- Web and desktop send Normal as an explicit
- addedbugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
on Oct 4, 2026 Same failure through a second path, with no mobile client involved. This one is likely common with orchestration v2:
- A thread is created by an agent through the MCP tool
t3_thread_launchwithmodelSelection: {instanceId, model}and nooptions. Later turns come from an outside client throughorchestration.dispatchCommand/message.dispatchwithoutmodelSelection. The live Claude query therefore runs withsettings: {}in itsqueryIdentity. - The agent starts a background task (a Monitor waiting on a build).
- The user then types in the web client. That message carries
options: [{id: "fastMode", value: false}], givingsettings: {fastMode: false}, a differentselectionKey.openQuerytreats it as a model/setting change and throwsClaudeBackgroundWorkBlocksQueryReplacementError. Three messages in a row were refused; nothing had changed.
Cause as seen in the bundle:
compileClaudeModelSelectionaddsfastModetosettingsonly when the option is present, so an explicit default value and a missing option produce differentqueryIdentitystrings. Normalizing defaults before building the identity would cover both the mobile/web case in this issue and the MCP/outside-client case.Version: 0.0.46-nightly.20261003.2632 (Linux server), claudeAgent / claude-opus-5-5. The thread then broke for good the way #15103 describes (details there).
- A thread is created by an agent through the MCP tool
Before submitting
Area
apps/server
Steps to reproduce
gh pr checks --watch).Expected behavior
The message goes to the live Claude process. Nothing about the model or its settings changed.
Actual behavior
Every message fails with:
The thread accepts nothing until the background work ends on its own. Stop doesn't help either, which is tracked separately.
Cause
The two clients send the same Normal speed differently. Mobile omits
fastMode. The web composer always addsfastMode: false(withImplicitFastModeDefaultinapps/web/src/components/chat/composerProviderState.tsx).compileClaudeModelSelection(apps/server/src/claudeModelOptions.ts) keeps the two apart:settingsis{}for one and{ fastMode: false }for the other, soqueryIdentitydiffers.openQueryinClaudeAdapterV2.tsthen sees a selection change. Since #14726 it refuses that change withClaudeBackgroundWorkBlocksQueryReplacementErrorwhenever background work runs. Before #14726 it silently restarted the process, which killed the work.From the affected thread's run records:
modelSelection.options[{ id: "fastMode", value: false }]Suggested fix
Compile an unset
fastModeasfalsefor models that support it. That is the Normal speed the web composer already sends, so both clients produce the same settings and the same query identity. It's a one-line change incompileClaudeModelSelection; the guard from #14726 stays as it is. I can open the PR.Impact
Blocks work completely
Version or commit
t3@0.0.46-nightly.20261004.2644 (main 8fb068c)
Environment
Linux host (systemd service). macOS desktop client and the mobile app on the same thread.
Screenshots, recordings, or supporting files
The model and effort are unchanged (Claude Opus 5.5, Medium · 1M), yet each message is refused as a setting change:
Once the shell ended, the next message showed a context handoff from Claude Opus 5.5 to Claude Opus 5.5, again with no model change:
Workaround
Keep sending from the client the background work was started from, or wait until the work ends.
Related