[BUG] Skill frontmatter model: override ignored when Claude invokes the skill via the Skill tool (works when user types /skill-name)
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?
The SKILL.md frontmatter model: override is path-dependent: it works when the user types /skill-name, but is silently not applied when Claude invokes the same skill via the Skill tool mid-turn — even though the harness registers the override on both paths (toolUseResult.model and a command_permissions attachment both carry the resolved model ID).
This narrows #45191: the field is parsed and resolved everywhere, applied only on the user-typed path. Since model-triggered invocation is the default path for model-invocable skills, cost-control uses of model: silently fail most of the time.
Reproduced identically on 2.1.153 (CLI, Windows 11) and 2.1.215 (desktop app). Full annotated transcript evidence in first comment below.
What Should Happen?
Per the frontmatter reference (https://code.claude.com/docs/en/skills#frontmatter-reference): "Model to use when this skill is active. The override applies for the rest of the current turn" — regardless of whether the skill was invoked by the user or by Claude via the Skill tool.
Error Messages/Logs
Steps to Reproduce
- Create a probe skill at
~/.claude/skills/which-model/SKILL.md(nocontext: fork):
---
name: which-model
description: Diagnostic probe — report which model is actually sampling right now.
model: opus
---
- Set the session to a non-Opus model (tested with claude-fable-5).
- Ask Claude in prose to invoke the skill — it fires Skill(which-model) mid-turn.
- In a later turn, type /which-model yourself.
- Read ground truth from
message.modelon assistant entries in the session transcript (~/.claude/projects/<proj>/<session>.jsonl) — this field comes from the API response.
Result: step 3 keeps sampling on the session model (override ignored); step 4 switches to claude-opus-4-8 for the rest of the turn (override honored, reverts next prompt as documented).
Control: context: fork + model: is honored on BOTH invocation paths — so parsing and model resolution are fine; the gap is the inline mid-turn application.
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.153 (Claude Code) — reproduced identically on 2.1.215 (desktop app)
Platform
Other
Operating System
Windows
Terminal/Shell
VS Code integrated terminal
Additional Information
follow up info here
Showing cached comments. Read the full discussion on GitHub ↗
3 Comments
Full evidence promised in the report. Ground truth throughout =
message.modelonassistantentries in the session transcript.jsonl— this field comes from the API response and demonstrably flips per-message (see the fork control at the end), so it's trustworthy.1. Both paths in one session (2.1.215, desktop app) — annotated transcript
2. The override registers even on the failing path (2.1.153) — raw transcript lines
Skill invoked via the Skill tool; the harness resolves
model: opus→claude-opus-4-8in two places:…yet every subsequent assistant entry in the same turn keeps the session model:
So the gap is specifically the inline mid-turn application of an already-registered override — not frontmatter parsing or model-name resolution.
3. Fork control — proves the measurement method
A skill with
context: fork+model: haiku, invoked via the Skill tool from an Opus session, produced a subagent transcript (~/.claude/projects/<proj>/subagents/agent-*.jsonl) whose assistant entries are allclaude-haiku-4-5-20251001(5/5). Same harness, same transcript field, override honored — confirmingmessage.modelreflects real sampling.Adjacent observation
The system prompt's "You are powered by the model named …" line is frozen at session start — after
/modelswitches or a successful skill override it keeps claiming the original model (observed claiming Fable while Opus served, and vice versa). Harmless for sampling, but it makes the model's self-report useless for verifying any of this; transcriptmessage.modelis the only reliable signal. Happy to split into its own issue if preferred.Environment detail
claude-fable-5[1m],claude-opus-4-8[1m], Anthropic API (the Platform dropdown above should read "Anthropic API")context: fork— works on the versions above)i am the human that told it to post this.
i have manually open the session log in notepad
manually analyzed it like a caveman back in the punch card days.
and can confirm this issue is real and correct.... yes i also read the whole issue
Can confirm the underlying mechanism has regressed further since this was filed — as of v2.1.220,
model:override now fails even for the invocation path this issue says should work (direct, user-typed/skill-name), not just via theSkilltool. Tested with a minimal throwaway skill (model: claude-opus-4-8), invoked by typing the slash command myself: thecommand_permissionsattachment in the transcript correctly recorded the requested override, but every subsequentassistantentry in that turn still ran on the session's default model.Filed a broader follow-up covering this (both
commands/*.mdandskills/SKILL.md, both invocation paths, with full transcript evidence and a working-vs-broken version comparison): #81318. Linking here since it directly extends this report's premise. If you've hit this too, a 👍 there helps signal impact.