Session reconnect silently discards an explicit `/model` choice and falls back to the global default
Summary
Attaching to a session by remote control from another machine, then resuming it
on its host, replaces the model the user set with /model with the global
default from settings.json, with no notification.
Every remote_session_change in my transcript re-injects a model identity
attachment. That appears to be the mechanism: the model is re-asserted on each
remote attach/detach, and the value re-asserted is the global default rather
than the session's explicit choice. The UI and the session metadata continue to report the
user's chosen model, so the substitution is invisible from inside the app. In my
case a session ran 293 calls on claude-fable-5-1 over ~16 hours while the app
reported claude-opus-5 throughout.
Two further defects kept it from being correctable once it happened:set_session_model reported success without taking effect, and only a /model
run inside the session could fix it.
Environment
- Claude Code desktop app 1.52386.0 (Code tab), Claude Agent SDK 0.3.266
- Windows 11 Pro 10.0.26200
~/.claude/settings.jsonhad"model": "fable[1m]"as the global default- Session was in a git worktree, created by the app's worktree flow
Steps to reproduce
- Set a global default model in
~/.claude/settings.json, e.g.
"model": "fable[1m]".
- Start a session. It runs on that default, as expected.
- In the session, run
/model claude-opus-5. The session confirms
`Set model to claude-opus-5 and subsequent replies useclaude-opus-5`. Correct so far.
- Attach to that session by remote control from a second machine, work in it,
then return to the machine the session actually lives on and resume it there.
Each attach/detach emits a remote_session_change.
- Send another message.
In my case the /model in step 3 was run from a laptop connected remotely to a
session hosted on my desktop; the revert happened when I moved back to the
desktop and picked the session up locally.
Expected
The session keeps the model chosen in step 3, or — if a reconnect genuinely
cannot preserve it — says clearly that the model has been reset to the default.
Actual
The session silently reverts to the global default. The transcript records the
change only as an injected model attachment:
{"attachment":{"type":"model","identity":{
"modelId":"claude-fable-5-1",
"marketingName":"Fable 5.1",
"knowledgeCutoff":"June 2026"},
"text":"You are powered by the model named Fable 5.1..."},
"type":"attachment","timestamp":"2026-09-11T05:49:23Z"}
Nothing in the UI changes. The model picker continued to display Opus 5, and the
app's own session metadata kept returning it:
{
"sessionId": "local_4fcccd9e-c5cf-43ee-b773-f0a85117b61d",
"lastActivityAt": "2026-09-11T21:38:49.932Z",
"model": "claude-opus-5"
}
Every reply for the next 16 hours was claude-fable-5-1. The only way to detect
this is to read message.model in the raw transcript under~/.claude/projects/**/*.jsonl.
Timeline from my transcript (Pacific)
| Time | Event |
|---|---|
| 09-10 20:00:45 | /model claude-opus-5 — confirmed, and the next reply is Opus. |
| 09-10 22:49:23 | remote_session_change — I moved from the laptop back to the desktop hosting the session. Silent revert to Fable 5.1. No command from me. |
| 09-10 22:50 - 09-11 14:38 | 293 replies, all claude-fable-5-1. App reports claude-opus-5 throughout. |
| 09-11 ~11:40 | Global default changed to opus. No effect on the running session. |
| 09-11 ~11:45 | set_session_model with claude-opus-5 returns "will use claude-opus-5 from its next turn on". No effect. |
| 09-11 13:19 - 14:38 | 63 further Fable calls after that reported success. |
| 09-11 15:16:36 | /model run inside the session — `Set model to opus (claude-opus-5) . |claude-opus-5` reply since the revert. |
| 09-11 15:45:23 | First
Related defects surfaced while diagnosing this
set_session_modelreports success but does nothing to a running session.
It returned "will use claude-opus-5 from its next turn on"; the session made
63 more Fable calls over the following three hours. It should either apply or
report that it cannot.
- **The app's session metadata is not derived from what the session actually
sends.** get_session returning "model": "claude-opus-5" for a session
making Fable calls means no UI surface can be trusted for this.
- An in-session
/modelwas the only effective fix. If that is by design,
the other paths should say so rather than reporting success.
Impact
Users pacing a usage allowance against a specific model cannot tell which model
they are actually spending it on. In my case, deduplicated by message.id from
the transcript: 293 calls, 161.9M cache-read, 3.2M cache-write, 7.7K input and
566K output tokens were billed as a model I had explicitly switched away from,
while the app told me otherwise.
Suggested fixes
- Persist an explicit per-session model across reconnects, at higher precedence
than the global default.
- If a reset is unavoidable, surface it in the conversation the way other state
changes are surfaced, not only as a silent attachment.
- Derive the displayed model from what the session actually sends, or reconcile
the two.
- Make
set_session_modelfail loudly when it cannot affect a running session.
Happy to supply the raw transcript lines if useful.