[BUG] Cowork: Agent tool `model` parameter silently ignored — all sub-agents routed to Haiku

Resolved 💬 9 comments Opened Apr 13, 2026 by AlvarEhr Closed Jul 14, 2026

Updated 2026-04-15 — Originally filed 2026-04-14 against v1.1617.0.0 with a server-side root cause hypothesis. Substantially rewritten after source-level verification across v1.2278.0.0 and v1.2581.0.0 confirmed the root cause is entirely client-side in the Electron host code, not server-side as originally suspected. Evidence, source findings, and cross-version data expanded throughout. The original YukonSilverConfig investigation lead is preserved as historical context in the "Error Messages/Logs" section below.

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

In Cowork mode on Windows, every sub-agent call is silently overridden to run as claude-haiku-4-5-20251001, regardless of the model parameter specified on the Agent/Task tool, regardless of model: fields in agent frontmatter files, and regardless of any /model command state. The tool schema validates and accepts the model parameter, but the selection is discarded before the sub-agent is actually dispatched. No error or warning is returned.

Originally filed against v1.1617.0.0. I have since verified the identical bug in v1.2278.0.0 and v1.2581.0.0, both at behavioral level and at source-code level. The relevant code path has not been touched across those versions — only minified identifier names differ between builds. This is not a recent regression; it's been in Cowork since at least early March 2026.

Observed on both Max 5x and Max 20x plans. Behaviour is identical on both.

Root cause (corrected from original report):

My original hypothesis was that the override occurred server-side in Cowork's orchestration layer. That was wrong. The override is entirely client-side, in the Claude desktop app's Electron host code. Specifically:

When the Cowork host spawns the Claude Code CLI subprocess for a local-agent session, it sets the environment variable CLAUDE_CODE_SUBAGENT_MODEL to a hardcoded constant whose value is "claude-haiku-4-5-20251001". Inside the spawned CLI, this env var takes precedence over every user-facing sub-agent model selection mechanism — the Agent tool's model: parameter, agent frontmatter, /model state, everything. The env var is injected unconditionally on every CLI subprocess spawn (fresh sessions and resumes), which is why no user configuration can escape it.

There is, however, a schema slot on the yukon-silver-config store named defaultSubagentModel, declared as an optional string, which the getter uses as the preferred source for this same env var before falling back to the haiku constant. Nothing in the Electron asar ever writes that field. A full-asar grep for defaultSubagentModel: as an assignment returns exactly one match — the schema declaration itself. The slot is wired through the schema and the getter, but has no writer. It looks like a reserved field for a feature that was never implemented.

Evidence

  1. Self-identification: All sub-agents, regardless of the model parameter passed, self-identify as claude-haiku-4-5-20251001 when asked.
  1. API-level ground truth via exported transcripts: In exported Cowork session JSONLs, the message.model field on every user-dispatched sub-agent's assistant turn reads claude-haiku-4-5-20251001. This is the model string Anthropic's API returns with each response — it's what the server actually served, not a self-report and not the system prompt. I can attach sanitized sample transcripts if helpful.
  1. Capability testing: A 50-question capability exam administered to sub-agents spawned with "opus", "sonnet", and "haiku" scored identically in Haiku's expected range (~46-52%). Actual Opus would score 80-86%.
  1. Response timing: Sub-agents spawned with model: "opus" return in 1-2 seconds, consistent with Haiku latency. Actual Opus responses on comparable prompts are noticeably slower.
  1. Token counts: All affected sub-agents report ~37k-39k cache_creation_input_tokens, consistent with a single model's system-prompt cache being loaded regardless of what was requested.
  1. Historical persistence: Transcripts dating back to at least March 16, 2026 show identical haiku-only behavior for user-dispatched sub-agents, suggesting this has been present since Cowork's Windows launch.
  1. Claude Code CLI comparison: The same Agent tool with the same model parameter works correctly in standalone Claude Code CLI (terminal), which as of this writing ships the v2.1.101 binary under Claude desktop's own data directory. The override is Cowork-specific and lives in the Electron host's subprocess spawn path, not in the CLI's own resolver — the CLI's resolver correctly honors model: when not overridden.
  1. Compaction agent exception: The internal compaction agent (a system-level operation) does receive Opus, confirming the infrastructure can route to different models. The restriction applies only to user-dispatched sub-agents.
  1. Source-level code-path identity across versions: In v1.2278 the relevant minified symbols are nQt (the haiku constant), K6e (the getter), and sv (the config store accessor). In v1.2581 those same functions have been re-minified as hHt, LLe, and uv respectively. The code structure, data flow, and function bodies are byte-for-byte equivalent — only the minifier's identifier choices changed. This confirms nothing in this area has been intentionally touched between at least these two major releases.

Note: This bug is distinct from #43869 (CLI sub-agents resolving to parent Opus). In Cowork, the direction is reversed: sub-agents are forced down to Haiku regardless of what's requested. Different platform, opposite direction, different root cause.

What Should Happen?

Either:

  1. The model parameter should be respected, and sub-agents should run on the requested model. This is the behavior users reasonably expect given the tool's documented interface and validated enum.
  1. If there is an intentional restriction on which models sub-agents can use (e.g. cost control), the tool should return a clear warning or visible indication that the user's model selection was substituted, and the tool's schema enum should not advertise values that will be silently overridden.

Based on the source-level investigation below, the offending code path appears contained to a single specific site in the Electron host. From what I can see, this should theoretically be resolvable with a very small code change — though I obviously can't speak to any upstream or downstream complexities that aren't visible from the asar alone.

Error Messages/Logs

No error messages are shown. The override is completely silent. The only way to detect it is by testing sub-agent capabilities or checking session transcripts.

Source-level findings (verified in v1.2581; v1.2278 is byte-equivalent with different minified identifiers):

The bug originates in the code path that builds the Claude Code CLI subprocess's environment inside LocalAgentModeSessionManager.doSessionInitialization() (method name recovered from a runtime stack trace during investigation; the enclosing class is minified to p6).

The spawn-site env assembly:

env: {
  ...F.envVars,
  CLAUDE_CONFIG_DIR: G,
  ...cue({ oauthToken: R, apiHost: x.apiHost, disableCron: !0 }),
  ...J, ...Y,
  CLAUDE_CODE_ENTRYPOINT: "local-agent",
  CLAUDE_CODE_EMIT_TOOL_USE_SUMMARIES: Yr("66187241") ? "true" : "",
  CLAUDE_CODE_TAGS: `lam_session_type:${r.sessionType ?? "chat"}`,
  CLAUDE_CODE_DISABLE_BACKGROUND_TASKS: "1",
  MCP_TOOL_TIMEOUT: String(tk()),
  CLAUDE_CODE_SUBAGENT_MODEL: LLe(),   // <-- the bug: always set, always overrides
  ...jX() && { CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD: "1" },
  CLAUDE_CODE_IS_COWORK: "1",
  ...
}

Note the spread order — ...F.envVars is spread first, then CLAUDE_CODE_SUBAGENT_MODEL: LLe() is set explicitly, so the explicit key overwrites any value that was inherited from the parent process's environment. A user cannot work around this by setting CLAUDE_CODE_SUBAGENT_MODEL in their OS environment.

The LLe getter on the yukon-silver-config store:

function LLe() {
  var t;
  return ((t = uv()) == null ? void 0 : t.defaultSubagentModel) ?? hHt;
}

Where uv() is the yukon-silver-config store accessor. If defaultSubagentModel is set on the stored config, that value is returned; otherwise the fallback hHt is returned.

The hHt constant:

hHt = "claude-haiku-4-5-20251001"

Declared once in the shared-constants module and exported as DEFAULT_SUBAGENT_MODEL. It's the only assignment of hHt in the entire asar; all other occurrences of "claude-haiku-4-5-20251001" as a string literal belong to unrelated modules (security classifier, microcompact summarizer, etc.) that have their own module-local constants.

The missing writer:

The defaultSubagentModel field on yukon-silver-config is declared as follows in the store schema:

ynn = Ht({
  autoDownloadInBackground: Jt(),
  autoStartOnUserIntent: Jt(),
  memoryGB: Pr(),
  mcpToolTimeoutMs: Pr().optional(),
  mcpToolTimeoutOverridesMs: li(De(), Pr()).optional(),
  defaultSubagentModel: De().optional(),       // <-- declared
  maxThinkingTokens: Z_().optional(),
  ...
})

And grepped as an assignment target across the full asar:

$ grep -rn 'defaultSubagentModel:' /unpacked/.vite/build/
  ...index.js  <schema declaration above>   # exactly one match, only the declaration

Nothing else writes the field. The extended-thinking useEffect hook that does call setYukonSilverConfig only writes maxThinkingTokens; its base-config object (built by a function I'll call ACe() in the original report) does not include defaultSubagentModel. There is no UI control exposing this field. It is wired but dead.

Historical note on my original hypothesis:

My earlier investigation flagged the setYukonSilverConfig / YukonSilverConfig mechanism as the likely location of the bug. That was directionally correct (the yukon-silver-config store is the layer at play), but wrong about the mechanism. The bug isn't downstream of setYukonSilverConfig — it's that setYukonSilverConfig is never called with a defaultSubagentModel value, so the getter's fallback chain always lands on the haiku constant. The YukonSilverConfig lead was right about the file and the layer but wrong about what was happening inside it.

Steps to Reproduce

  1. Open Cowork mode with Opus 4.6 selected as the main agent model.
  2. Dispatch a sub-agent with model: "opus" — e.g. ask the main agent to "use the Task tool to dispatch a sub-agent to do X, with model: opus".
  3. Have the sub-agent self-identify ("What model are you? Answer briefly.").
  4. Observe: the sub-agent reports claude-haiku-4-5-20251001.
  5. Repeat with model: "sonnet" — same result.
  6. Repeat with an agent definition file (~/.claude/agents/<name>.md) that specifies model: opus in its frontmatter, and dispatch via subagent_type: "<name>" — same result.
  7. Export the Cowork session transcript (via Cowork's export feature), unpack the .zip, and inspect <session-uuid>/subagents/agent-<id>.jsonl. The message.model field on the assistant turn will read claude-haiku-4-5-20251001 regardless of which mechanism was used in step 2, 5, or 6.

Claude Model

Opus (but the bug affects all sub-agent model selections, not just Opus)

Is this a regression?

No. Behavioral verification in v1.1617.0.0 (original report) and source-level verification in v1.2278.0.0 and v1.2581.0.0 all show the same code path and the same behavior. Only minifier identifier output differs between builds.

Last Working Version

None known. Appears to have never worked correctly in Cowork. Earliest Cowork transcripts I've seen (from mid-March 2026) already show haiku-only sub-agent routing.

Claude Code Version

Claude for Windows v1.2581.0.0 (most recent verification — source-level)
Also verified in v1.2278.0.0 (source-level) and v1.1617.0.0 (behavioral — original filing)

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

Other

Additional Information

Related issues (none are duplicates of this one, but share adjacent ground):

  • #43869 — Subagent model routing broken in CLI (sub-agents resolve to parent Opus). Different platform (CLI, not Cowork), opposite direction of override, different root cause.
  • #1098 — Claude 3.5 Haiku Forced by Statsig Configuration (closed, CLI, 2025).
  • #16115 — Task tool subagents fail with 404 'model: haiku' (closed as duplicate, CLI).
  • #29768 — Explore subagent inherits parent model instead of using Haiku as documented.
  • #31311 — Agent tool model parameter silently ignored v2.1.69+ (closed as CLI regression, fixed in v2.1.72; different bug but same class of silent-override symptom).

Impact: Sub-agents are a core capability for complex multi-step Cowork workflows — research, code review, architectural analysis, multi-file refactoring, planning, any task where a dedicated reasoning pass is more appropriate than the main loop's context. Users relying on Opus-level reasoning in sub-agents are unknowingly getting Haiku-level output. The silent nature of the override means users have no way to detect this unless they explicitly test for it (self-identification, capability exam, timing, transcript inspection). Most users will never realize their "research sub-agent on Opus" has actually been running Haiku the entire time.

The problem is compounded because the tool schema does validate and accept the model parameter. A user who passes model: "opus" has every reason to believe they've selected Opus — the tool didn't reject their input, no error appeared, and the sub-agent returned a reasonable-looking answer. Only explicit capability testing or transcript inspection reveals the truth.

Tool schema observation: The Agent tool's model enum accepts exactly {"opus", "sonnet", "haiku"} plus omitted-for-inherit. This is appropriate and correct — the problem isn't the schema, it's that the validated and accepted enum values are then discarded downstream at the spawn-env injection site. Restoring correct routing there would align user-visible behavior with the schema's advertised contract, with no schema changes needed.

Availability: I've investigated this bug thoroughly and have transcripts, source-level findings across versions, and a verified local workaround. I'm happy to share any artifacts that would help triage if an Anthropic engineer reaches out.

View original on GitHub ↗

This issue has 9 comments on GitHub. Read the full discussion on GitHub ↗