[BUG] Welcome banner shows subscription default model instead of settings.json-pinned model
Environment
- Claude Code version: 2.1.170 (latest as of filing)
- Platform: Windows 11 Pro (native installer,
~/.local/bin/claude.exe) - Subscription: Claude Max
Bug description
When a model is pinned via ~/.claude/settings.json ("model": "claude-fable-5[1m]"), the startup/welcome banner shows the subscription default model instead of the pinned one, while the session actually runs the pinned model.
What I see at launch in the same session:
Claude Code v2.1.170
▝▜█████▛▘ Opus 4.8 with high effort · Claude Max ← banner (wrong)
Fable 5 | CTX: 0% | ... ← statusline (correct)
The statusline (which receives model.display_name via the statusLine stdin JSON) correctly reports Fable 5, and the session genuinely runs claude-fable-5[1m] — only the banner is wrong.
Root cause (from reading the bundled JS in the binary)
There are two divergent model-resolution paths:
- Query path — resolves CLI
--modelflag →initialMainLoopModel(seeded fromsettings.jsonat startup) →ANTHROPIC_MODELenv → settings. This path correctly picks up the settings-pinned model, so requests run on it.
- Welcome banner — renders
modelDisplayName(useCurrentModel())where the hook reads React app-state:mainLoopModelForSession ?? mainLoopModel ?? defaultModel(). The app-state is initialized withmainLoopModel: nulland is never seeded from the settings-pinned model (it only gets set on an in-session/modelswitch). So at first paint the banner falls through todefaultModel()— the subscription default (Opus 4.8) — and appends the effort suffix from settings, producing "Opus 4.8 with high effort" regardless of the actual pinned model.
This explains why the bug is invisible to users on the default model (the fallback happens to match) and only shows up when model is pinned in settings.json.
Steps to reproduce
- In
~/.claude/settings.jsonset"model": "claude-fable-5[1m]"(any non-default model works). - Launch
claudeinteractively with no--modelflag. - Banner header shows the subscription default (e.g. "Opus 4.8 with high effort · Claude Max") instead of the pinned model. The statusline
model.display_nameand actual completions use the pinned model.
Expected behavior
The welcome banner should show the same model the session will actually use — i.e. fall back through the same resolution chain as the query path (settings-pinned model before the subscription default), or seed app-state mainLoopModel from initialMainLoopModel at startup.