Skip to content

AddFrontmatter deletes every frontmatter block after the first #138

Description

@matt-edmondson

What's wrong

When a document has more than one frontmatter block, AddFrontmatter (Frontmatter/Frontmatter.cs:173-184) does the following:

  1. It calls ExtractFrontmatter(input), which returns only frontmatterObjects.First() (:138).
  2. It merges the new properties into that first block.
  3. It calls ReplaceFrontmatter(input, combined), which rebuilds the document from the body that comes after all blocks (ExtractFrontmatterObjects(input, out string body), :208).

So the properties from the second and later blocks are neither merged nor preserved; they are simply removed from the document.

Repro

string doc = "---\ntitle: T\n---\n---\ntags: [x]\n---\nbody\n";
Frontmatter.AddFrontmatter(doc, new() { ["author"] = "Me" });
  • Actual: ---\ntitle: T\nauthor: Me\n---\nbody. The tags value is gone.
  • Expected: title, tags and author in the header, which is what CombineFrontmatter produces for the same input.

Why it matters

A call meant to add one property deletes existing properties, and nothing reports it. Multiple stacked blocks are exactly the input CombineFrontmatter exists to handle, and the recent hardening in AddFrontmatter ("frontmatter it cannot read is left alone", from the #132/#133 fix) shows the intent is never to lose data. This path still loses data the library can read.

Suggested fix

In AddFrontmatter, fold every parsed block together (ExtractFrontmatterObjects + CombineFrontmatterObjects, as CombineFrontmatter does at :87-91) before merging in the new properties. If any non-blank block fails to parse, return the input unchanged, matching the existing guard.

Acceptance: the repro keeps tags, with a regression test covering two stacked blocks.

Activity

  1. matt-edmondson commented on Sep 27, 2026

    @matt-edmondson
    ContributorAuthor

    Triage

    • Category: Bug
    • Priority: High. A call meant to add one property silently deletes existing, perfectly parseable properties. It happens on ordinary input (stacked blocks, which CombineFrontmatter exists to handle) and nothing reports it.
    • Area / suggested owner: AddFrontmatter / ExtractFrontmatter / ReplaceFrontmatter in Frontmatter.cs.
    • Related: CombineFrontmatter deletes a frontmatter block it cannot parse when another block does parse #139 has the same root cause (the body from after all blocks, combined with a header built from a subset of them). Fix both together.
    • Duplicates / in progress: None found. No open PR covers this.
    • Next step: In AddFrontmatter, fold all parsed blocks with ExtractFrontmatterObjects + CombineFrontmatterObjects before merging, and bail out unchanged if any non-blank block fails to parse. Add a two-stacked-blocks regression test.

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

readyFully specified; implement as written

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions