[BUG] Skill frontmatter model: override applied on user-typed invocation but silently ignored when invoked via the Skill tool (registers in both cases)
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 demonstrably registers the override on both paths (the Skill tool result and a command_permissions attachment both carry the resolved model ID).
This narrows #45191 (closed as not planned, reported as "model: has no effect at runtime"): the field is parsed and resolved correctly everywhere, and applied on the user-typed path — only the mid-turn application is missing. Since model-triggered invocation is the default path for model-invocable skills, cost-control uses of model: (route mechanics to a cheaper model, cap an expensive session) silently fail most of the time.
Reproduced identically on 2.1.153 (CLI, Windows 11) and 2.1.215 (desktop app).
What Should Happen?
Per the 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.
Actual:
- User-typed
/skill-name: override applied for the rest of the turn, reverts on next prompt ✅ (matches docs) - Claude invokes via Skill tool: override registered (
toolUseResult.model,command_permissions.model) but sampling continues on the session model ❌
Error Messages/Logs
# Both paths in one session (2.1.215, desktop app) — transcript excerpt, annotated.
# Ground truth = message.model on assistant entries in the session .jsonl.
L25 assistant model=claude-fable-5 <-- Claude invokes Skill(which-model); skill content loads
L29 assistant model=claude-fable-5 (all post-load sampling stays on session model)
L31 assistant model=claude-fable-5
L34 assistant model=claude-fable-5
L37 assistant model=claude-fable-5 "override did NOT take effect"
L41 user <-- user types /which-model
L46 assistant model=claude-opus-4-8 (override applied immediately)
L48 assistant model=claude-opus-4-8
L49 assistant model=claude-opus-4-8 "override TOOK EFFECT this time"
L53 user <-- next user prompt
L54 assistant model=claude-fable-5 (session model resumes, as documented)
# The override REGISTERS even on the failing path (2.1.153) — raw transcript lines:
"toolUseResult":{"success":true,"commandName":"land-branch","model":"claude-opus-4-8"}
"attachment":{"type":"command_permissions","allowedTools":[],"model":"claude-opus-4-8"}
# ...yet every subsequent assistant entry in the same turn keeps the session model:
L184 assistant model=claude-fable-5 (03:32:41 — after the skill + override registered at 03:32:32)
L185 assistant model=claude-fable-5
L186 assistant model=claude-fable-5
L188 assistant model=claude-fable-5
L189 assistant model=claude-fable-5
Steps to Reproduce
- Create a probe skill at
~/.claude/skills/which-model/SKILL.md(nocontext: fork):
``yaml``
---
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-modelyourself. - Read ground truth from
message.modelonassistantentries in the session transcript (~/.claude/projects/<proj>/<session>.jsonl). This field comes from the API response, and it correctly flips per-message in other scenarios (see the fork control below), so it is trustworthy.
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).
Claude Model
Other
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
Anthropic API
Operating System
Windows
Terminal/Shell
VS Code integrated terminal
Additional Information
Control — context: fork + model: works on both paths: a skill with context: fork + model: haiku, invoked via the Skill tool from an Opus session, produced a subagent transcript whose assistant entries are all claude-haiku-4-5-20251001 (5/5). So frontmatter parsing, model resolution, and per-message message.model accuracy are all fine — the gap is specifically the inline mid-turn application of an already-registered override.
Adjacent observation: the system prompt's "You are powered by the model named …" line is frozen at session start — after /model switches 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 model self-report useless for verifying overrides; transcript message.model is the only reliable signal. Happy to split this into its own issue if preferred.
Session models involved: claude-fable-5[1m], claude-opus-4-8[1m].
Related: #45191 (closed/locked; this narrows it), #17283 (Skill tool + context: fork — works on the versions above). Supersedes #79654 (my earlier filing that bypassed the issue form).
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗