[BUG] Welcome banner shows subscription default model instead of settings.json-pinned model

Open 💬 0 comments Opened Jun 9, 2026 by auazze

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:

  1. Query path — resolves CLI --model flag → initialMainLoopModel (seeded from settings.json at startup) → ANTHROPIC_MODEL env → settings. This path correctly picks up the settings-pinned model, so requests run on it.
  1. Welcome banner — renders modelDisplayName(useCurrentModel()) where the hook reads React app-state: mainLoopModelForSession ?? mainLoopModel ?? defaultModel(). The app-state is initialized with mainLoopModel: null and is never seeded from the settings-pinned model (it only gets set on an in-session /model switch). So at first paint the banner falls through to defaultModel() — 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

  1. In ~/.claude/settings.json set "model": "claude-fable-5[1m]" (any non-default model works).
  2. Launch claude interactively with no --model flag.
  3. 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_name and 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.

View original on GitHub ↗