◉CLAUDE LABJP
●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…
Articles/Claude Code
⟐ Claude Code/2026-05-22Intermediate

Why Claude Code Breaks Your MDX Frontmatter With YAML Errors (and How to Fix It)

If you have ever let Claude Code mass-produce MDX articles, sooner or later your build dies with a cryptic YAML error. Nested double quotes, stray colons, comma-joined tag entries — here is what is actually happening and the five patterns I trust in production.

Claude Code264MDXYAMLNext.js8Troubleshooting20

If you have ever let Claude Code generate a few dozen MDX articles in a row, you have probably seen this: the first thirty pieces ship cleanly, and then one day npm run build dies with YAMLException: end of the stream or a document separator is expected. You re-read the body of the article a dozen times before noticing that the real culprit is sitting up in the frontmatter.

I lost half a day to this when I first wired Claude Code into the auto-publishing pipeline behind Dolice Labs, the four AI blogs (Claude Lab, Gemini Lab, Antigravity Lab, Rork Lab) I run as an indie developer. After more than ten years building wallpaper and relaxation apps for the App Store and Google Play — Dolice's apps have crossed 50 million cumulative downloads since 2014, mostly monetized through AdMob — I am used to environment-specific failures, but the way YAML parsers react to LLM output is in a category of its own.

The symptom — your build dies on "YAML parse failed"

On a Next.js + MDX + gray-matter style stack, you tend to see something like this:

[Error: YAMLException: can not read a block mapping entry; a multiline key may not be an implicit key
 at line 4, column 1:
    description: "Run Claude Code in "Plan mode" to preview…"
    ^]

Sometimes the wording is unexpected end of the stream, sometimes tag suffix cannot contain flow indicator characters. None of it looks broken in the editor, so the first instinct is to blame the build environment. In practice, almost every occurrence comes down to one thing: there is a character in the frontmatter that the YAML parser cannot interpret the way Claude assumed it would.

When it bites — three patterns Claude steps on the most

These are the three failure modes I have actually seen in production. Claude Code reaches for quotation marks when it wants to sound natural, and that is where the trouble lives.

Pattern 1: a double-quoted value with a double quote inside

description: "Run Claude Code in "Plan mode" before applying changes…"

Syntactically the parser stops at the second ", then treats the rest as a stray key. To embed a literal " inside a double-quoted YAML scalar you have to write \". Claude rarely escapes proactively because, linguistically, it is just writing the next sensible sentence.

Pattern 2: a bare colon followed by a space, mid-value

title: "Claude Code 3.0: Release Highlights"

The quoting saves you here, but the moment Claude forgets the outer quotes — for example writing title: 3.0: Highlights — the parser sees two key: value pairs and falls over. Long descriptions with mismatched quotes amplify the problem because the parser keeps consuming downstream lines looking for the missing closing quote.

Pattern 3: a tag array element that contains a comma

tags: ["Claude Code, Cowork", "TypeScript"]

This is technically valid YAML, but it silently breaks any downstream tags.includes("Claude Code") filter. No build error, no warning — just a tag page that mysteriously refuses to list the article. That is the worst kind of bug because no log will ever tell you about it.

Why this happens — quoting rules YAML inherited from C

In YAML 1.2, a double-quoted scalar uses C-style escaping. To embed a literal " you must write \". Single-quoted scalars use a different convention: you escape a single quote by doubling it ('') and everything else is taken literally. Claude has both rules in its training data, but it cannot reliably tell which convention applies at the moment it is writing the next token, so it defaults to whatever sounds natural in prose.

The fix is not to make Claude obey YAML escaping. The fix is to narrow the surface where it can possibly go wrong.

Five patterns I trust in production

In Dolice Labs I publish multiple articles per site per day across four sites, fully driven by Claude Code. After getting burned a few times, I settled on these five conventions, ordered from most reliable to most flexible.

Fix 1: never use straight double quotes inside a quoted scalar

For Japanese articles I instruct Claude to substitute 「」 (full-width corner brackets) whenever it wants to quote something inside a frontmatter value. For English I tell it to swap straight double quotes for curly quotes (" ") or single quotes. Either way the parser never sees an ambiguous " inside another ".

description: "Run Claude Code in 'Plan mode' before applying changes"

Fix 2: switch the outer wrapper to single quotes

If you genuinely need straight double quotes in the value, wrap the entire scalar in single quotes.

description: 'Run Claude Code in "Plan mode" before applying changes'

To include a literal ' inside, double it (''). For a site like ours with parallel Japanese and English versions, the simplest rule is "JA uses corner brackets, EN uses single-quoted outer scalar." That keeps the convention consistent across translators.

Fix 3: use a block scalar (| or >)

When a description is long, or includes multiple quoted phrases, block scalars are the cleanest option:

description: >
  Run Claude Code in "Plan mode" to dry-run the change.
  This is why the official docs keep repeating that the
  Edit tool is destructive.

> folds line breaks into spaces; | preserves them. For Open Graph descriptions I prefer > because the final value ends up on one line in the HTML head.

Fix 4: avoid colon + space mid-value

When you want a colon-like break in a title or description, prefer an em dash or a full-width colon over : followed by a space.

# Risky
title: "Claude Code 3.0: Release Highlights"
# Safer
title: "Claude Code 3.0 — Release Highlights"

Fix 5: no commas inside a tag value

One tag, one concept. Resist the temptation to write "Claude Code, Cowork" as a single tag for the sake of brevity. Split them, and let your filter handle the OR condition.

tags: ["Claude Code", "Cowork", "TypeScript"]

Prevention — validate the frontmatter before push

At Dolice Labs we run a tiny Python script over every staged MDX before git push. It carves out the frontmatter and feeds it to yaml.safe_load. If anything throws, the push aborts long before Cloudflare ever sees the commit.

import re
import sys
import yaml
from pathlib import Path
 
def check(path):
    text = Path(path).read_text(encoding="utf-8")
    m = re.match(r"^---\n(.*?)\n---\n", text, re.DOTALL)
    if not m:
        return f"{path}: no frontmatter"
    try:
        yaml.safe_load(m.group(1))
    except yaml.YAMLError as e:
        return f"{path}: {e}"
    return None
 
errors = [e for e in (check(p) for p in sys.argv[1:]) if e]
if errors:
    print("\n".join(errors))
    sys.exit(1)

Wire this into your push step (python3 yaml_check.py content/articles/**/*.mdx) and the failure mode collapses from "Cloudflare build error fifteen minutes later" into "line and column printed locally in under a second."

The lesson — narrow the surface, do not police the LLM

What I learned, after several Cloudflare builds died in a row, is that asking an LLM to follow YAML escaping rules perfectly is the wrong battle. It is far cheaper to narrow the set of characters Claude is allowed to use in frontmatter values, and validate mechanically before push. Corner brackets instead of double quotes, em dashes instead of colons, no commas inside tags — those three rules, plus a 30-line Python validator, ended the issue for me across all four Dolice Labs sites. Thanks for reading.

Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

⟐ Claude Code2026-10-13
The Same Approval Prompt for 'cd, git add, git commit' Every Time — Following It to a Design Choice, Not a Missing Setting
With a compound command like cd /path && git add && git commit, Claude Code keeps asking for approval even with allow rules in place. Reading the official permissions page next to a GitHub report shows that cd combined with git prompts by design. Here is how to tell, in what order to check, and four ways to cut the prompts.
⟐ Claude Code2026-09-10
When Every Request Fails With Not signed in to the Cloud gateway, Check the Version Before the Config
A Claude Code 2.1.265 regression broke every request for gateway and proxy setups. I use it as an excuse to build a small snapshot of the auth input surface, so the next time something breaks you can tell config from version in seconds.
⟐ Claude Code2026-09-04
The ~/.claude.json Rollback Is Fixed in v2.1.259. Putting Back What It Erased Is Still Your Job
Concurrent sessions used to silently roll back each other's ~/.claude.json changes. v2.1.259 fixed that, but nothing restores the trust settings and MCP servers you already lost. Here is how I find and rebuild them, with a small key-path diff script.