[BUG] Output styles with YAML frontmatter not injected into system prompt

Status Open
Reported on v2.1.104
Maintainer reply ✓ Yes — bcherny
Activity 38 comments · opened Apr 13, 2026
💡 Likely answer: A maintainer (bcherny, collaborator) responded on this thread — see the highlighted reply below.

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

  1. 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

  1. Set the output style in ~/.claude/settings.local.json:
{ "outputStyle": "canary" }
  1. Start a new interactive session: claude
  1. 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: false to 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)

View original on GitHub ↗

38 Comments

raye-deng · 4 months ago

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:

  • AI generating imports for packages that are in package.json but not installed (node_modules out of sync)
  • AI calling APIs that are defined in OpenAPI specs but have been deprecated in production
  • AI assuming environment variables exist that are set locally but not in CI/CD

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 L1

It validates that the code's assumptions match the runtime environment.

alberduris · 4 months ago

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.

alberduris · 4 months ago

Still present in v2.1.109.

alberduris · 4 months ago

Still present in v2.1.110.

alberduris · 4 months ago

Still present in v2.1.111.

alberduris · 4 months ago

Still present in v2.1.112.

alberduris · 4 months ago

Still present in v2.1.114 (also v2.1.113, which moved the CLI to a native binary — behavior unchanged).

alberduris · 4 months ago

Still present in v2.1.116.

alberduris · 4 months ago

Still present in v2.1.117.

alberduris · 4 months ago

Still present in v2.1.118.

alberduris · 4 months ago

Still present in v2.1.119.

alberduris · 4 months ago

Still present in v2.1.120 and v2.1.121.

alberduris · 4 months ago

Still present in v2.1.122 and v2.1.123.

alberduris · 4 months ago

Still present in v2.1.126.

alberduris · 3 months ago

Still present in v2.1.128.

alberduris · 3 months ago

Still present in v2.1.129 and v2.1.131.

alberduris · 3 months ago

Still present in v2.1.132.

alberduris · 3 months ago

Still present in v2.1.133.

alberduris · 3 months ago

Still present in v2.1.136, v2.1.137 and v2.1.138.

alberduris · 3 months ago

Still present in v2.1.139.

alberduris · 3 months ago

Still present in v2.1.140, v2.1.141 and v2.1.142.

alberduris · 3 months ago

Still present in v2.1.143.

alberduris · 3 months ago

Still present in v2.1.144.

alberduris · 3 months ago

Adding richer diagnostic data from v2.1.144 since the "Still present in vX" pings are getting stale:

1. /context confirms the selection is registered but rollup is too coarse to inspect the difference. Both the broken and the workaround configurations show identical System 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 --debug shows the selection metadata is honored but content injection is silent. With outputStyle: canary active, the request log contains:

[DEBUG] [API REQUEST] /v1/messages source=repl_main_thread:outputStyle:custom

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 --debug level — 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-instructions frontmatter 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).

alberduris · 3 months ago

Still present in v2.1.145 and v2.1.146.

alberduris · 3 months ago

Still present in v2.1.147 and v2.1.148.

alberduris · 3 months ago

Still present in v2.1.149 and v2.1.150.

alberduris · 3 months ago

Still present in v2.1.152.

alberduris · 3 months ago

Still present in v2.1.153.

dcrn · 3 months ago

Just ran into this. Using the exact same name as the filename seems to make it work, set name: canary instead of name: Canary. The other fields (description, keep-coding-instructions) are all fine as-is.

alberduris · 3 months ago

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.

alberduris · 3 months ago

Still present in v2.1.157 and v2.1.158.

alberduris · 2 months ago

Still present in v2.1.159 and v2.1.160.

justin-zeno · 2 months ago

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.

alberduris · 2 months ago

Still present in v2.1.161.

alberduris · 2 months ago

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:

  1. Case-insensitive match, or match on filename only and ignore \name\ for resolution.
  2. If keeping strict matching, fail loud: log a warning at startup and surface it in \/doctor\ and \/debug-your-config\.
  3. Docs at https://code.claude.com/docs/en/output-styles do not document the filename↔\name\ coupling — should be noted there.
alberduris · 1 month ago

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:

# .claude/output-styles/ccbugcheck.md
---
name: CcBugCheck
description: probe
---
Begin every response with the literal token CANARY_ALIVE on its first line.
# .claude/settings.json
{ "outputStyle": "ccbugcheck" }

Then claude -p "say the word ok":

  • name: CcBugCheck → model replies just ok (the rule is ignored → the body never reached the system prompt)
  • name: ccbugcheck → model replies CANARY_ALIVE then ok (body injected)

So the loader matches the frontmatter name against 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:

  1. Match case-insensitively, or resolve by filename and ignore name for resolution.
  2. If strict matching is kept, fail loud — warn at startup and surface it in /doctor.
  3. Document the filename↔name coupling at https://code.claude.com/docs/en/output-styles (currently undocumented).
bcherny collaborator · 15 days ago

Thanks for the detailed report, and thanks @dcrn for spotting the key detail. I re-ran your exact steps on 2.1.233 (macOS) and this is a name mismatch, not a lost frontmatter body:

  • name: Canary in the frontmatter + "outputStyle": "canary" in settings → responds normally (what you saw)
  • name: Canary + "outputStyle": "Canary" → "CANARY ALIVE"
  • name: canary + keep-coding-instructions: false + "outputStyle": "canary" → "CANARY ALIVE"

The outputStyle setting must equal the style's name, and the file name only becomes the name when the frontmatter has no name field. When there is no name, "canary" (the file name) matches; when you set name: Canary, the style is now called "Canary" and "canary" no longer matches (the lookup is case-sensitive), so Claude Code silently falls back to the default style. Docs: https://code.claude.com/docs/en/output-styles#create-a-custom-output-style ("The file name becomes the style name unless you set name in the frontmatter").

We agree this is confusing: the status line echoes whatever is in your settings even when it doesn't match any style, and there is no warning about the fallback. We're considering surfacing a warning when outputStyle doesn't resolve to a known style, and possibly matching names case-insensitively / by file name as a fallback. Frontmatter fields like keep-coding-instructions and description work today once the names line up.

🤖 Generated with Claude Code