Skip to content

[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

@vitalyiegorov

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

  1. Use a Claude thread from the mobile app, and have the agent start a background shell or a background agent (here gh pr checks --watch).
  2. Without changing the model, effort or any option, send a message to the same thread from the web or desktop client while that work still runs.

Expected behavior

The message goes to the live Claude process. Nothing about the model or its settings changed.

Actual behavior

Every message fails with:

Claude is still running background agents or commands, and this model or setting change would end them. Wait for them to finish, or press Stop, then send the message again.

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 adds fastMode: false (withImplicitFastModeDefault in apps/web/src/components/chat/composerProviderState.tsx).

compileClaudeModelSelection (apps/server/src/claudeModelOptions.ts) keeps the two apart: settings is {} for one and { fastMode: false } for the other, so queryIdentity differs. openQuery in ClaudeAdapterV2.ts then sees a selection change. Since #14726 it refuses that change with ClaudeBackgroundWorkBlocksQueryReplacementError whenever background work runs. Before #14726 it silently restarted the process, which killed the work.

From the affected thread's run records:

Runs Sent from modelSelection.options Result
1–14 mobile none completed
15–16 server (continuation after the update restart) none completed; run 16 started the background shell
17–19 web [{ id: "fastMode", value: false }] failed with the error above

Suggested fix

Compile an unset fastMode as false for 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 in compileClaudeModelSelection; 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:

Messages refused as a model or setting change while a background shell runs

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:

Context handoff from Claude Opus 5.5 to Claude Opus 5.5

Workaround

Keep sending from the client the background work was started from, or wait until the work ends.

Related

Activity

  1. vitalyiegorov commented on Oct 4, 2026

    @vitalyiegorov
    ContributorAuthor

    PR opened: #15473. For models that expose fast mode, an unset fastMode now compiles as Normal, which is what the web composer already sends. Mobile and web then produce the same query identity, and the #14726 guard stays as it is.

  2. juliusmarminge commented on Oct 4, 2026

    @juliusmarminge
    Member

    Note

    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. withImplicitFastModeDefault in apps/web/src/components/chat/composerProviderState.tsx adds 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 no fastMode option.
    • compileClaudeModelSelection (apps/server/src/claudeModelOptions.ts) keeps them apart: an omitted value stays out of settings, while false becomes { fastMode: false }. queryIdentity includes that object, so the two sends don't match.
    • openQuery in ClaudeAdapterV2.ts only reuses the live process when the identity matches. While that process has background work and isn't stopping, the mismatch raises ClaudeBackgroundWorkBlocksQueryReplacementError, which is the message you saw. Opus 5.5 exposes fastMode in 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:

    A maintainer will decide on the fix direction.

  3. added
    bugSomething is broken or behaving incorrectly.
    via-triageFiled through npx t3 triage
    on Oct 4, 2026
  4. CoreInfusion commented on Oct 4, 2026

    @CoreInfusion

    Same failure through a second path, with no mobile client involved. This one is likely common with orchestration v2:

    1. A thread is created by an agent through the MCP tool t3_thread_launch with modelSelection: {instanceId, model} and no options. Later turns come from an outside client through orchestration.dispatchCommand / message.dispatch without modelSelection. The live Claude query therefore runs with settings: {} in its queryIdentity.
    2. The agent starts a background task (a Monitor waiting on a build).
    3. The user then types in the web client. That message carries options: [{id: "fastMode", value: false}], giving settings: {fastMode: false}, a different selectionKey. openQuery treats it as a model/setting change and throws ClaudeBackgroundWorkBlocksQueryReplacementError. Three messages in a row were refused; nothing had changed.

    Cause as seen in the bundle: compileClaudeModelSelection adds fastMode to settings only when the option is present, so an explicit default value and a missing option produce different queryIdentity strings. 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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions