anthropics / anthropics/claude-code

Session reconnect silently discards an explicit `/model` choice and falls back to the global default

オープン
#93,757 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る
area:cost area:model bug has repro platform:windows
主要言語
Python
スター
145k
フォーク
23.1k
PR マージ指標
PR 指標を取得中

説明

## 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.

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

調査の方向性

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.

索引モデルが issue の本文から書いたものです。

評価

領域
backend, cli
issue の種類
バグ
難易度
5/5
見積もり時間
1週間以上
活発さ
活発
明瞭さ
おおむね明確
初心者へのやさしさ
45/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。