anthropics / anthropics/claude-code
Session reconnect silently discards an explicit `/model` choice and falls back to the global default
- Dominant language
- Python
- Stars
- 145k
- Forks
- 23.1k
- PR merge metrics
- PR metrics pending
Description
## 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.json` had `"model": "fable[1m]"` as the global default
- Session was in a git worktree, created by the app's worktree flow
## Steps to reproduce
1. Set a global default model in `~/.claude/settings.json`, e.g.
`"model": "fable[1m]"`.
2. Start a session. It runs on that default, as expected.
3. In the session, run `/model claude-opus-5`. The session confirms
``Set model to `claude-opus-5` `` and subsequent replies use
`claude-opus-5`. Correct so far.
4. 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`.
5. 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:
```json
{"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:
```json
{
"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)` ``. |
| 09-11 15:45:23 | First `claude-opus-5` reply since the revert. |
## Related defects surfaced while diagnosing this
1. **`set_session_model` reports 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.
2. **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.
3. **An in-session `/model` was 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_model` fail loudly when it cannot affect a running session.
Happy to supply the raw transcript lines if useful.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start at the remote_session_change and set_session_model entry points, using ~/.claude/settings.json as the global-default reference. Compare the session's persisted model with message.model in ~/.claude/projects/**/*.jsonl across a reconnect. Done means an explicit /model choice survives reconnects, or any reset is surfaced, set_session_model reports its real effect, and displayed metadata matches the model actually used.
Written by the indexing model from the issue text.
Assessment
- Domain
- backend, cli
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100