[BUG] Auto-compaction in a team-lead background session silently switched the main loop to teammateDefaultModel (durable until manual /model)

Open 💬 0 comments Opened Jun 12, 2026 by koiakoia

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet (closest: #49816 — compaction model conflated with CLAUDE_CODE_SUBAGENT_MODEL; #56989 — post-compaction behavior corruption in team agents; #36670 / #43782 — teammates resolve model variants incorrectly. None cover the lead's own model being switched.)
  • [x] This is a single bug report
  • [x] I am using the latest version of Claude Code (incident on 2.1.174; behavior re-examined on 2.1.175)

What's Wrong?

A long-running session acting as an agent-team lead (TeamCreate + active teammates, sessionKind: bg) hit auto-compaction. The first API request after the compaction rebuild was sent to a different model — the one configured as teammateDefaultModel in ~/.claude.json — instead of the session's pinned model. The switch was silent (no UI indication, no log entry) and durable: every main-chain response after the compaction used the wrong model, for ~8 minutes / 77 assistant messages, until the operator noticed degraded output quality and manually ran /model. It does not self-restore.

During the silent window the downgraded model continued executing the lead's queue — in our case including credential rotation against production infrastructure. The model swap was only discovered later by auditing transcript message.model fields.

Timeline from the session JSONL (timestamps UTC, sanitized):

02:45:48  system  subtype=compact_boundary  {trigger: "auto", preTokens: 210641, durationMs: 171973}
02:45:48  user    isCompactSummary=true     (the rebuilt summary prompt)
02:45:48  attachments (memory files + deferred_tools_delta — no model calls)
02:45:59  assistant  message.model = claude-sonnet-4-6   ← FIRST rebuilt request; session was pinned to another model
   ...      (every main-chain assistant message is claude-sonnet-4-6; 77 total)
02:53:10  user    <command-name>/model</command-name>
                  stdout: "Set model to Fable 5 and saved as your default for new sessions"
02:53:24  user    (operator message noting output quality dropped and asking why Sonnet was running)
02:53:34  assistant  message.model = claude-fable-5      ← restored ONLY by operator action

What Should Happen?

The post-compaction request rebuild should preserve the session's resolved model. teammateDefaultModel should only ever apply to spawned teammates, never to the lead/main loop. If the harness ever does change the main-loop model for any reason, it should say so visibly rather than switching silently.

Evidence that this is model-resolution, not API fallback

  1. Zero error/retry/fallback entries anywhere in the transcript — not just at the boundary. The documented fallback path logs an error before switching; nothing of the kind is present. The only entries between compact_boundary and the first wrong-model response are the compact summary and passive attachments.
  2. The response model field is the requested model — so the client requested the wrong model on the rebuilt call. This is not server-side substitution.
  3. The switch is durable and persists across many requests until an explicit /model — inconsistent with transient per-request fallback, consistent with the session's resolved-model state being recomputed wrongly at the rebuild.
  4. Source of the wrong model: the literal string configured at ~/.claude.json → teammateDefaultModel was the only place that model appears in any config on the machine. The saved default (settings.json → model) was already the correct pinned model at compaction time (saved by /model 84 minutes earlier), ruling out a "re-read saved default" explanation.

Steps to Reproduce

Honest caveat up front: one controlled repro attempt did NOT reproduce the switch — but the attempt necessarily differed from the incident in ways that plausibly matter (listed below). Filing anyway because the incident evidence is unambiguous about what happened, even though the trigger conditions are not yet isolated.

Incident conditions (v2.1.174):

  1. Session started by the background-job daemon (sessionKind: "bg", entrypoint: "cli" in the JSONL)
  2. Model set interactively via /model early in the session ("saved as your default for new sessions" — both session model and saved default = the pinned model)
  3. Session created an agent team (TeamCreate) and had two live teammates spawned via the Agent tool
  4. ~/.claude.json had teammateDefaultModel: "sonnet" set
  5. Conversation grew to ~210k tokens → auto-compaction fired
  6. First rebuilt request and all subsequent main-chain requests went to claude-sonnet-4-6 until manual /model

Controlled repro attempt (v2.1.175, did NOT reproduce):

  1. Set ~/.claude.json → "teammateDefaultModel": "haiku" (distinctive — nothing else on the machine resolves to Haiku)
  2. claude -p --model claude-sonnet-4-6 --dangerously-skip-permissions --max-turns 80 "<scripted prompt>" — the scripted prompt: TeamCreate a team, spawn one teammate via Agent with no model param, then Read ten ~80 KB files one at a time to force auto-compaction
  3. The teammate correctly resolved to claude-haiku-4-5-20251001 (so the haiku swap was live and applied where it should)
  4. Auto-compaction fired (compact_boundary, trigger: "auto", preTokens: 175535)
  5. Result: all post-compaction main-chain responses stayed claude-sonnet-4-6 — no switch to haiku

Differences between incident and repro attempt that may explain the non-repro:

  • 2.1.174 vs 2.1.175 — and 2.1.174's own changelog fixed "background sessions inheriting another session's ANTHROPIC_* provider env (gateway URL, custom headers, /model aliases) from the shell that started the background daemon," so model-state leakage around bg sessions was an active bug family right at the incident version
  • /model interactive pin vs --model CLI flag — the flag may pin through a stronger resolution path
  • daemon bg job vs headless claude -p — pre-warmed background workers had state-bleed fixes in 2.1.172 ("reading another directory's project settings") and 2.1.175 ("claimed after sitting idle")
  • incident model id carried a [1m] suffix; the repro model id was plain (2.1.173 fixed [1m] normalization issues)

Error Messages/Logs

(no errors emitted — that's part of the bug; relevant JSONL excerpts above)

Claude Model

Other — session pinned via /model to a non-default model; switched to the teammateDefaultModel value after auto-compaction

Is this a regression?

I don't know

Claude Code Version

2.1.174 (incident) / 2.1.175 (repro attempt)

Platform

Anthropic API

Operating System

Other Linux (CachyOS, kernel 7.0.11-1-cachyos-bore)

Terminal/Shell

Non-interactive/CI environment (background job session, sessionKind: "bg", entrypoint: "cli"; shell fish)

Additional Information

  • The session was a team lead with two live teammates spawned via the Agent tool (TeamCreate + named team), which is presumably why teammateDefaultModel was in scope at all.
  • Possibly related mechanism-wise: #49816 reported that compaction shares model-selection machinery with subagents (CLAUDE_CODE_SUBAGENT_MODEL). This report is the team-flavored sibling: the post-compaction main loop appears to resolve through the teammate default.
  • Same session also started on the wrong model for one turn (the previous session's saved default) before the operator's /model — may or may not share a root cause, mentioned for completeness.
  • ~/.claude.json had both a saved default model and teammateDefaultModel set; settings.json had "model": "<pinned model>".

View original on GitHub ↗