Skip to content

Invalid YAML frontmatter triggers a b2id re-stamp loop (file grows every reindex; desktop can loop on it) #75

Description

@samkeen

Found while property-testing #18 (PR #74) — probing the edge the generators deliberately avoid.

Symptom

A note whose frontmatter fails to YAML-parse gets a fresh b2id: line inserted on every reindex. The file grows without bound, collects one dead ULID per pass, and B2 repeatedly writes a note it was only ever allowed to touch once.

Repro

cat > vault/bad.md <<'MD'
---
	: :
title: [unclosed
---
A body line.
MD
b2 reindex && b2 reindex && b2 reindex
# bad.md now carries THREE b2id lines (76 → 109 → 142 bytes in the probe run)

Root cause

note::extract_fields is deliberately best-effort: unparseable frontmatter still round-trips, the fields just come back empty — including b2id: None. Ingest gates stamping on fields.b2id, so an unreadable-but-present id (or a just-stamped one inside still-invalid YAML) reads as "missing" every single pass:

stamp inserts b2id: <new ulid> → surrounding YAML is still invalid → next parse still yields b2id: None → stamp again.

Two invariants break: "the only write B2 makes of its own accord is stamping a missing b2id" (it stamps N of them), and reindex idempotency (full ≡ incremental can't hold when every pass rewrites the file — the note's identity churns ULIDs, so even the index never stabilizes).

Severity — elevated by #73

Before #73 this needed manual reindexes. Since project-on-pulse (#73), the desktop reconciles vault-changed by projecting — and a stamp is itself a vault change: stamp → pulse → project → YAML still invalid → stamp again → pulse… an unbounded write loop (~1 write per debounce window) for any vault containing one bad-YAML note. One hand-edited unclosed bracket in frontmatter is enough to trigger it.

Fix directions (needs a decision)

Stamping is B2's one autonomous write — it should be gated on "definitively absent", never on "couldn't parse":

  • (a) Conservative raw scan fallback: when the YAML parse fails, line-scan the frontmatter text for a b2id: key before stamping (and use its value for projection). Keeps the note indexed and its identity stable; the malformed YAML remains the user's to fix.
  • (b) Never stamp unparseable frontmatter: treat a note whose frontmatter exists but won't parse as unstampable — skip the write, surface it like the existing skipped unreadable-file channel. Safest write posture, but the note drops out of (or churns in) the index.

(a) preserves the most value and matches the byte-honest philosophy; either way the fix should land with a props.rs property over arbitrary/invalid frontmatter (stamp-at-most-once; second reindex writes nothing) and the invalid-YAML case added to the #18 generators.

Refs

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions