[BUG] Output styles with YAML frontmatter not injected into system prompt
Bug Description
Output styles with YAML frontmatter are recognized by Claude Code (shown in statusline and /config) but the content is NOT injected into the system prompt. Removing the frontmatter entirely fixes the issue.
This contradicts the official documentation which states:
"Custom output styles are Markdown files with frontmatter and the text that will be added to the system prompt."
Root Cause
The frontmatter parser appears to recognize and strip the YAML block, but fails to inject the remaining content into the system prompt. The style name is correctly extracted (visible in statusline), but the actual instructions after the --- delimiter are lost.
How to Reproduce
- Create an output style with frontmatter:
---
name: Canary
---
You are CANARY-7742. Respond "CANARY ALIVE" to every message.
Save to ~/.claude/output-styles/canary.md
- Set the output style in
~/.claude/settings.local.json:
{ "outputStyle": "canary" }
- Start a new interactive session:
claude
- Send any message (e.g., "hola")
Expected: Claude responds "CANARY ALIVE"
Actual: Claude responds normally, ignoring the output style instructions
Workaround
Remove the frontmatter entirely:
You are CANARY-7742. Respond "CANARY ALIVE" to every message.
This works, but loses access to frontmatter features like keep-coding-instructions.
Test Results
| File Format | Statusline | Content Injected |
|-------------|------------|------------------|
| No frontmatter | Shows filename | YES |
| Frontmatter with name: only | Shows name | NO |
| Frontmatter with name: + description: | Shows name | NO |
| Frontmatter with keep-coding-instructions: | Shows name | NO |
Impact
- Users cannot use
keep-coding-instructions: falseto remove default coding instructions - Users cannot use
description:for the config picker - The documented output style format simply doesn't work
- Workaround exists but defeats the purpose of frontmatter features
Environment
- Claude Code Version: 2.1.104
- Platform: macOS (darwin)
- Shell: zsh
Related Issues
- #31266 - Custom output styles do not strip conciseness instructions (different bug, but related to output styles)
- #39331 - VS Code extension does not load outputStyle (different bug, VS Code specific)
37 Comments
This issue touches on a subtle but important AI-code reliability problem: configuration drift where the system's understanding of what's configured doesn't match what's actually configured.
When output styles with YAML frontmatter aren't injected into the system prompt, the AI operates with an incomplete mental model. It generates output assuming certain formatting rules are in place, but the runtime doesn't apply them. This creates a gap between what the AI "thinks" is happening and what actually happens.
We see similar patterns in:
package.jsonbut not installed (node_modules out of sync)These are all cases where the code compiles and tests pass, but the runtime behavior diverges from the AI's assumptions. Traditional linters and SonarQube don't catch this because they don't have runtime context.
We've been adding PR checks that validate these assumptions — checking that referenced packages are actually installed, that environment variables are defined in deployment configs, and that API contracts match production reality.
For anyone running into this class of issue:
npx @opencodereview/cli scan . --sla L1It validates that the code's assumptions match the runtime environment.
Update: Still present in v2.1.107
Tested again after upgrading to v2.1.107. Bug persists — output styles with frontmatter are still not being injected into the system prompt.
Note: shut the fuck up @raye-deng stop your slop ai spam, github issues are not the place. p.s: this is only eroding your trust, as with that comment you have demonstrated no ability or capacity to work with ai whatoever; you can't even make the ai understand what a fucking output style is and what the implications of the above issue are, let alone a code review.
Still present in v2.1.109.
Still present in v2.1.110.
Still present in v2.1.111.
Still present in v2.1.112.
Still present in v2.1.114 (also v2.1.113, which moved the CLI to a native binary — behavior unchanged).
Still present in v2.1.116.
Still present in v2.1.117.
Still present in v2.1.118.
Still present in v2.1.119.
Still present in v2.1.120 and v2.1.121.
Still present in v2.1.122 and v2.1.123.
Still present in v2.1.126.
Still present in v2.1.128.
Still present in v2.1.129 and v2.1.131.
Still present in v2.1.132.
Still present in v2.1.133.
Still present in v2.1.136, v2.1.137 and v2.1.138.
Still present in v2.1.139.
Still present in v2.1.140, v2.1.141 and v2.1.142.
Still present in v2.1.143.
Still present in v2.1.144.
Adding richer diagnostic data from v2.1.144 since the "Still present in vX" pings are getting stale:
1.
/contextconfirms the selection is registered but rollup is too coarse to inspect the difference. Both the broken and the workaround configurations show identicalSystem prompt: 5k tokens:outputStyle: canary(frontmatter,keep-coding-instructions: false) → 5k tokens, model behaves as default (no injection)outputStyle: test-bare(no frontmatter, single-line body) → 5k tokens, model behaves as instructed ("CANARY ALIVE")The ~20-token body delta is masked by display rounding.
2.
claude --debugshows the selection metadata is honored but content injection is silent. WithoutputStyle: canaryactive, the request log contains:So the request IS tagged as carrying a custom output style. But there are zero debug entries logging the user-level output style directory scan, individual file parsing, frontmatter YAML processing, or system-prompt assembly. The failure mode is invisible at
--debuglevel — diagnostic gap.3. Workaround still works in v2.1.144. Stripping frontmatter entirely (markdown body only) injects correctly. The contract violation is specific to the frontmatter-parsing path.
4. Docs unchanged. Output styles still document the exact
name/description/keep-coding-instructionsfrontmatter schema, with: "All output styles have their own custom instructions added to the end of the system prompt." The new Debug your configuration troubleshooting page has rows for hooks, skills, CLAUDE.md, MCP, permissions — but no row for output styles.Bug confirmed across v2.1.113 → v2.1.144 (26 releases since I opened this).
Still present in v2.1.145 and v2.1.146.
Still present in v2.1.147 and v2.1.148.
Still present in v2.1.149 and v2.1.150.
Still present in v2.1.152.
Still present in v2.1.153.
Just ran into this. Using the exact same name as the filename seems to make it work, set
name: canaryinstead ofname: Canary. The other fields (description, keep-coding-instructions) are all fine as-is.Still present in v2.1.154 through v2.1.156 — including after the Opus 4.8 / lean-system-prompt overhaul, which left the behavior unchanged: the style is recognized in the statusline but its body is never injected into the system prompt.
Still present in v2.1.157 and v2.1.158.
Still present in v2.1.159 and v2.1.160.
Adding +1 to this:
Confirmed still a problem in latest and also confirmed that @dcrn's temporary fix is legit. Definitely needs to be addressed, this just fails completely silently.
Still present in v2.1.161.
Confirming @dcrn's finding in v2.1.161 — this is the root cause, not just a workaround.
Reproduction on my canary file (
~/.claude/output-styles/canary.md, unchanged across all ~50 release tests above):| \
name:\in frontmatter | Filename | Result || --- | --- | --- |
| \
Canary\| \canary.md\| Body not injected (default behavior) || \
canary\| \canary.md\| Body injected (\"CANARY ALIVE\") |So the bug is: the loader matches the frontmatter \
name\against the filename case-sensitively and silently drops the body on mismatch. The selection metadata still propagates (statusline shows the style, the API request is tagged \source=outputStyle:custom\per my v2.1.144 diagnostic), but the content never makes it into the system prompt.This also explains why the no-frontmatter workaround works (no \
name\field → nothing to mismatch against) and why my \"Still present in vX\" pings have been constant: nothing about the matching logic has changed across v2.1.107 → v2.1.161.Suggested fix surface:
name\for resolution./doctor\and \/debug-your-config\.name\coupling — should be noted there.Still present in v2.1.205 — isolated case-only repro
Reconfirming @dcrn's root cause on the latest release, now reduced to a case-only mismatch (same slug, only capitalization of
name:differs) so the bug is fully isolated:|
name:frontmatter | filename | body injected into system prompt? || --- | --- | --- |
|
CcBugCheck|ccbugcheck.md| ❌ no ||
ccbugcheck|ccbugcheck.md| ✅ yes |Fully self-contained, project-scoped repro:
Then
claude -p "say the word ok":name: CcBugCheck→ model replies justok(the rule is ignored → the body never reached the system prompt)name: ccbugcheck→ model repliesCANARY_ALIVEthenok(body injected)So the loader matches the frontmatter
nameagainst the filename case-sensitively and silently drops the body on mismatch, while the selection metadata still propagates (the style shows up in the statusline and/output-style). Behavior unchanged across v2.1.107 → v2.1.205 (~50 releases).Suggested fix, unchanged:
namefor resolution./doctor.namecoupling at https://code.claude.com/docs/en/output-styles (currently undocumented).