Magic Tools
Hands-OnBy CooconAugust 6, 2026559 views3 min read

Which Model Is Claude Code Actually Using? settings.json vs ANTHROPIC_MODEL vs --model, Tested

Why this deserves scrutiny

A wrong model configuration does not error — the session runs fine, just on a different model. In GitHub issue #82466, a user configured Fable in ~/.claude/settings.json, ran multi-agent workloads for a full day silently on Sonnet, and had to discard everything.

The official docs describe the model field in settings.json, the ANTHROPIC_MODEL environment variable, the --model flag, and the /model command separately — but never one table saying who wins when they coexist. Here are the measured results.

Method: hard evidence from modelUsage

In headless mode with --output-format json, the modelUsage key in the result is the model ID you were actually billed for — more reliable than any UI display or the model's self-description (what a model says about itself can be shaped by system prompts and does not count as evidence):

claude -p "reply ok" --output-format json | python3 -c \
  "import json,sys; print(list(json.load(sys.stdin)['modelUsage'].keys()))"

The tested priority matrix

Environment: macOS + Claude Code v2.1.220. Each row is an independent experiment in a clean directory, evidence taken from modelUsage:

Configuration Actually used Conclusion
Project .claude/settings.json set to haiku, nothing else haiku ✅ project-level settings honored
settings set to claude-fable-5[1m] (1M-context suffix) claude-fable-5[1m] ✅ the [1m] suffix form works too
settings haiku + env ANTHROPIC_MODEL=sonnet sonnet env var overrides settings
settings haiku + flag --model sonnet sonnet flag overrides settings
env haiku + flag --model sonnet sonnet flag overrides env var

The resulting priority chain (high → low):

--model flag  >  ANTHROPIC_MODEL env  >  project settings.json  >  built-in default

It matches the Unix intuition that configuration closer to the invocation wins — but until now that was intuition. Now it is evidence.

About that Windows issue

Issue #82466 reports a different layer: the user-level ~/.claude/settings.json (note: not project-level) being ignored on Windows 11 + PowerShell, and /model <exact-id> in interactive sessions replying "Kept model as X" and refusing to switch. The reporter ruled out env vars, project overrides, and every known on-disk location, and suspects some client-side UI state that never hits disk.

We could not reproduce it on macOS (every layer worked, as the table shows), and the issue has no official response at the time of writing. If your model looks wrong on Windows: do not assume you misconfigured it — collect evidence with the modelUsage method above, then add your environment details to that issue. Every cross-environment report moves the fix closer.

Practical recommendations

  • CI / scripts: pass --model explicitly — highest priority, smallest scope, immune to any persisted state
  • Project-wide default: use the project's .claude/settings.json (checked into the repo, shared with the team) — verified reliable
  • Verification: whenever a model "feels wrong", run one --output-format json call and read modelUsage; in interactive sessions, run /model with no argument and trust the highlighted item in the picker — the issue reporter found the "Kept model as X" text misleading, while the picker highlight reflects the real current state
  • Cost-sensitive setups: modelUsage also carries token counts and cost, handy for auditing bills

Scope and boundaries

  • Test environment is in the scope card above; the matrix covers headless (-p) with project-level settings / env / flag. The user-level ~/.claude/settings.json and interactive /model switching are outside this article's tested scope (the former is unsafe to mutate on a production machine, the latter is discussed in the issue)
  • Bedrock / Vertex channels have their own model ID schemes and env vars; conclusions here apply to direct Anthropic API/subscription only
  • Priority ordering is stable design semantics and should hold across versions; we will update the status here once the Windows issue is fixed

Get field notes like this every Saturday

Subscribe to Dev Breakfast: daily AI coding picks at 8:00, plus a Saturday roundup of this week's hands-on tests with Claude Code / Codex / local models. Written in Chinese.

Related Articles

Claude Code "Prompt is too long" vs "maximum context length": Why Auto-Compact Works for One and Not the Other (Tested)

Claude Code decides a request was too long by matching the error text. If the backend says prompt is too long or input is too long for requested model, it auto-compacts the conversation and retries — invisible to you. DeepSeek and OpenAI-style gateways say This model's maximum context length is …, which it doesn't recognize: you get API Error: 400 on every turn. Manual /compact works; the better fix is to declare the real window with CLAUDE_CODE_MAX_CONTEXT_TOKENS (non-claude- model IDs) or CLAUDE_CODE_AUTO_COMPACT_WINDOW (claude- IDs) so it compacts before hitting the limit. Tested on Claude Code 2.1.285 against a local stub.

claude-codedeepseek+6
pitfallsOct 5, 20267 min
122

Claude Code Stuck on a Spinner With No Response: It Waits 6 Minutes per Attempt, and 10 Retries Can Hang It for Over an Hour (Tested)

When Claude Code spins without output or sits at Retrying in 0s, the backend is usually not sending anything. Tested on 2.1.285 (API key + ANTHROPIC_BASE_URL): with no response headers it waits 6 minutes (360 s) per attempt before timing out — setting API_TIMEOUT_MS to 600000 or 900000 doesn't change that; only lower values work. With the default 10 retries it can hang for over an hour. A gateway that buffers the reply until generation finishes will never deliver a reply that takes over 6 minutes. Press Esc to interrupt. All tested against a local stub.

claude-codetroubleshooting+5
pitfallsOct 5, 20269 min
102

Claude Code 429 "Request rejected (429)": How Long It Retries, and Why retry-after Over 60 Seconds Fails Instantly (Tested)

On a 429, Claude Code retries up to 10 times over about 3 minutes. If retry-after is 60 seconds or less it waits the full time (10 s and 60 s both recovered in testing); at 61 seconds or more it does not retry at all and fails immediately with API Error: Request rejected (429). Gateway messages such as new-api's are shown verbatim; an empty body shows status code (no body). In interactive mode every prompt sends 2 requests, so rate-limit usage doubles. All tested against a local stub on Claude Code 2.1.285.

claude-codetroubleshooting+4
pitfallsOct 4, 20268 min
99
Claude Code MCP Shows Connected but 0 Tools: "Invalid result for tools/list" (ttlMs / cacheScope), Reproduced and Fixed

Claude Code MCP Shows Connected but 0 Tools: "Invalid result for tools/list" (ttlMs / cacheScope), Reproduced and Fixed

An MCP server shows as connected but exposes 0 tools, and the log says Invalid result for tools/list with ttlMs and cacheScope failing validation. Reproduced with a stub server on Claude Code 2.1.280, 2.1.285 and 2.1.288: the cause is not extra fields being rejected. The server negotiated MCP 2026-07-28 and then left out fields that revision requires (resultType, ttlMs, cacheScope); adding unknown fields works fine. Whether stdio uses the new protocol is decided by a remote flag that is off by default, so the same version breaks for some users and not others, and 2.1.280 fails the same way once negotiation is on. MCP_PROTOCOL_NEGOTIATION=legacy (also works in settings.json env) restores the tools; servers fix it by adding the three fields.

mcpclaude-code+4
pitfallsOct 3, 20268 min
92