--- create: 2026-07-09 update: 2026-07-09 author: thinkycx title: "Claude Code --resume Model Name Bug: Root Cause Analysis" description: Root cause analysis of Claude Code sending the Anthropic firstParty canonical model name instead of user-configured custom model name when resuming sessions via --resume. Covers env var loading timing, PROVIDER_MANAGED_ENV_VARS filtering, and AppState not persisting across session restore. category: troubleshooting tags: [claude-code, resume, model, env-vars, source-code-analysis] keywords: [claude-code, resume, model-resolution, ANTHROPIC_DEFAULT_OPUS_MODEL, getDefaultOpusModel, PROVIDER_MANAGED_ENV_VARS] refs: - https://github.com/anthropics/claude-code/issues/63435 --- # Claude Code --resume Model Name Bug: Root Cause Analysis > 中文版本: [Claude Code --resume 模型名称错误的根因分析](https://thinkycx.me/files/notes-pub/2026-07-09-claude-code-resume-model-bug-analysis-pub.md) ## 1. Background **Problem**: When resuming a session via `claude --resume `, the API receives model parameter `claude-opus-4-6` (Anthropic's firstParty canonical name) instead of the user-configured custom model name set via `ANTHROPIC_DEFAULT_OPUS_MODEL` environment variable. This causes the backend proxy to return HTTP 400. Running `/model` and re-selecting the same model immediately fixes the issue. **Goal**: Identify root cause and find a configuration-level workaround without waiting for an official fix. ## 2. Conclusion **On resume, Claude Code reverse-iterates the transcript's assistant messages to extract the `message.model` field (the API response's canonical name `claude-opus-4-6`), and directly uses it as the model parameter for the next request — instead of re-mapping through `ANTHROPIC_DEFAULT_*_MODEL` env vars to the user's custom model name.** 1. On resume, an internal function reverse-iterates messages to find the most recent assistant message's `message.model` field value 2. That field value is the Anthropic canonical model ID from the API response (e.g., `claude-opus-4-6`), not the custom name sent in the request 3. Claude Code directly uses this value as the model parameter for the next API request 4. The custom proxy backend doesn't recognize `claude-opus-4-6` and returns HTTP 400 5. Setting `ANTHROPIC_MODEL` env var bypasses this logic (guard condition at function entry) ## 3. Analysis ### 3.1 Model Resolution Priority Chain `getUserSpecifiedModelSetting()` priority order: ``` 1. getMainLoopModelOverride() ← /model command or --model CLI flag 2. process.env.ANTHROPIC_MODEL ← environment variable 3. settings.model ← model field in settings file 4. (fallback) getDefaultMainLoopModelSetting() → getDefaultSonnetModel()/getDefaultOpusModel() ``` ### 3.2 The Critical Branch in `getDefaultOpusModel()` ```typescript // src/utils/model/model.ts:105-116 export function getDefaultOpusModel(): ModelName { if (process.env.ANTHROPIC_DEFAULT_OPUS_MODEL) { return process.env.ANTHROPIC_DEFAULT_OPUS_MODEL // User's custom value ✓ } if (getAPIProvider() !== 'firstParty') { return getModelStrings().opus46 // 3P provider modelStrings } return getModelStrings().opus46 // firstParty: "claude-opus-4-6" ✗ } ``` `getAPIProvider()` only checks `CLAUDE_CODE_USE_BEDROCK` / `CLAUDE_CODE_USE_VERTEX` / `CLAUDE_CODE_USE_FOUNDRY` — **it does NOT inspect `ANTHROPIC_BASE_URL`**. Users with custom base URLs are misidentified as `firstParty`. ### 3.3 Environment Variable Filtering Mechanism `settings.json` env vars are injected into `process.env` via `applySafeConfigEnvironmentVariables()`, but pass through `filterSettingsEnv()`: ```typescript // src/utils/managedEnvConstants.ts const PROVIDER_MANAGED_ENV_VARS = new Set([ 'ANTHROPIC_DEFAULT_OPUS_MODEL', 'ANTHROPIC_DEFAULT_SONNET_MODEL', 'ANTHROPIC_DEFAULT_HAIKU_MODEL', 'ANTHROPIC_MODEL', 'ANTHROPIC_BASE_URL', 'ANTHROPIC_AUTH_TOKEN', // ... more ]) ``` When `CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST` is present in the spawn environment (Desktop App, VS Code extension, etc.), `withoutHostManagedProviderVars()` strips all the above variables — meaning model names configured in settings.json **are never injected into `process.env`**. ### 3.4 The True Root Cause: Model Extracted from Transcript (Reverse Engineering Verification) Through reverse engineering the compiled code, the true root cause function was identified (minified name `oR_`): ```javascript // Key function logic from decompiled/reverse-engineered code function oR_(messages) { // Guard conditions — skip this logic if any is true if (isClaudeAISubscriber() || process.env.ANTHROPIC_MODEL || !someCondition()) return; let knownModels = new Set(ALL_MODEL_CONFIGS.map(getCanonicalName)); // Reverse-iterate messages, find the most recent assistant message's model for (let i = messages.length - 1; i >= 0; i--) { let msg = messages[i]; if (msg?.type !== "assistant" || msg.isMeta || msg.message.model === SYNTHETIC_MODEL) continue; let model = msg.message.model; // Check if known model + allowed + passes filter if (!knownModels.has(getCanonicalName(model)) || !isModelAllowed(model) || someFilter(model)) return; return model; // ← Directly returns the model string from API response! } return; } ``` **The core problem**: ``` Resume flow: Start → loadConversationForResume() → load messages → oR_(messages) → finds assistant message.model = "claude-opus-4-6" → This value is directly used as the API request's model parameter → Proxy backend receives "claude-opus-4-6" instead of "aws.claude-opus-4.6" → 400 error ``` **Why `/model` fixes it**: After executing `/model`, it sets `AppState.mainLoopModel`, and subsequent model resolution goes through `parseUserSpecifiedModel` (alias → env var mapping), no longer relying on the transcript's model field. **Why `ANTHROPIC_MODEL` bypasses it**: The guard condition at the function entry explicitly checks `process.env.ANTHROPIC_MODEL` — when present, it returns immediately without executing the transcript scan. ### 3.5 Why `/model` Fixes It ```typescript // src/components/PromptInput/PromptInput.tsx:2028 setAppState(prev => ({ ...prev, mainLoopModel: model, // Set to 'opus' (alias) mainLoopModelForSession: null, })) ``` When selecting Opus in `/model`, `modelOptions.ts` returns the alias `'opus'` (for firstParty provider): ```typescript // src/utils/model/modelOptions.ts:136 value: is3P ? getModelStrings().opus46 : 'opus' ``` The alias `'opus'` goes through the alias branch in `parseUserSpecifiedModel`: ```typescript case 'opus': return getDefaultOpusModel() + (has1mTag ? '[1m]' : '') ``` If by this point the env var has been loaded via a subsequent `applyConfigEnvironmentVariables()` call, it correctly returns the custom model name. ### 3.6 Why `/model` Fixes It ```typescript // src/components/PromptInput/PromptInput.tsx:2028 setAppState(prev => ({ ...prev, mainLoopModel: model, // Set to 'opus' (alias) mainLoopModelForSession: null, })) ``` When selecting Opus in `/model`, `modelOptions.ts` returns the alias `'opus'` (for firstParty provider): ```typescript // src/utils/model/modelOptions.ts:136 value: is3P ? getModelStrings().opus46 : 'opus' ``` The alias `'opus'` goes through the alias branch in `parseUserSpecifiedModel`: ```typescript case 'opus': return getDefaultOpusModel() + (has1mTag ? '[1m]' : '') ``` If by this point the env var has been loaded via a subsequent `applyConfigEnvironmentVariables()` call, it correctly returns the custom model name. ### 3.7 Model String Configuration ```typescript // src/utils/model/configs.ts:72-77 export const CLAUDE_OPUS_4_6_CONFIG = { firstParty: 'claude-opus-4-6', // ← The fallback value on resume bedrock: 'us.anthropic.claude-opus-4-6-v1', vertex: 'claude-opus-4-6', foundry: 'claude-opus-4-6', } ``` ### 3.8 FAQ: Why Static Source Analysis Missed the True Root Cause Source analysis focused on explicitly named modules like `sessionRestore.ts`, `useMainLoopModel.ts`, `model.ts`, tracking the lifecycle of `AppState.mainLoopModel`. But the true root cause function `oR_`: 1. **React Compiler inlining**: The function is inlined into the REPL component after compilation. In source, it's likely an anonymous lambda or a closure inside a useEffect — not an explicitly named export 2. **Indirect call path**: It's not in `sessionRestore.ts` or `conversationRecovery.ts`, but in some hook or effect that runs after REPL render, before the query 3. **No explicit "resume" association**: The function itself doesn't contain the "resume" keyword. Its role is "infer which model to use from current messages" — triggered in both resume and plan mode scenarios 4. **Conditional triggering**: Only activates when `!isClaudeAISubscriber() && !process.env.ANTHROPIC_MODEL`. Without knowing these guard conditions, source-level searching cannot locate it **Lesson**: For compiled code (React Compiler / Bun bundler), reverse-engineering runtime behavior is more effective than static source analysis. Source analysis suits understanding design intent, but actual bugs may exist in post-compilation code paths. ### 3.9 `ANTHROPIC_MODEL` and the `[1m]` Context Window Issue Setting `ANTHROPIC_MODEL` bypasses the resume bug, but causes the context window to drop from 1M to 200k. **Reason**: `ANTHROPIC_MODEL` value is used as-is, bypassing the alias resolution system. In the normal path, the alias `'opus'` goes through `parseUserSpecifiedModel` which automatically appends the `[1m]` tag (indicating 1M context window). Setting the env var directly skips this. ``` Normal path: alias 'opus' → parseUserSpecifiedModel → getDefaultOpusModel() + '[1m]' → 1M ANTHROPIC_MODEL path: "aws.claude-opus-4.6" used as-is (no [1m]) → 200k ``` **Solution**: Manually append the `[1m]` tag to the value: ```json "ANTHROPIC_MODEL": "aws.claude-opus-4.6[1m]" ``` ## 4. Action - [x] Root cause identified - [x] Set `ANTHROPIC_MODEL` env var to bypass resume bug — must include `[1m]` suffix to retain 1M context - [ ] Export `ANTHROPIC_DEFAULT_*_MODEL` vars in shell profile (`~/.zshrc`) as alternative (doesn't rely on settings.json injection) - [ ] Comment on GitHub Issue #63435 with the PROVIDER_MANAGED_ENV_VARS filtering scenario ## References - [GitHub Issue #63435: Session resume fails with 400 "Invalid model name"](https://github.com/anthropics/claude-code/issues/63435) - [Claude Code Source: src/utils/model/model.ts](https://github.com/anthropics/claude-code) — getDefaultOpusModel, parseUserSpecifiedModel - [Claude Code Source: src/utils/managedEnvConstants.ts](https://github.com/anthropics/claude-code) — PROVIDER_MANAGED_ENV_VARS definition - [Claude Code Source: src/utils/managedEnv.ts](https://github.com/anthropics/claude-code) — applySafeConfigEnvironmentVariables, filterSettingsEnv