Skip to content

fix: tolerate malformed YAML frontmatter when parsing note metadata - #132

Merged
chhoumann merged 1 commit into
masterfrom
chhoumann/parser-malformed-fm
Jun 27, 2026
Merged

fix: tolerate malformed YAML frontmatter when parsing note metadata#132
chhoumann merged 1 commit into
masterfrom
chhoumann/parser-malformed-fm

Conversation

@chhoumann

Copy link
Copy Markdown
Owner

Tolerate malformed YAML frontmatter so a note's metadata parse never aborts on bad frontmatter; inline key:: value fields and tags still surface.

Why

PR #130 added a live-parse fallback in parseFrontmatterContent (src/parser.ts) for notes whose frontmatter range Obsidian's metadata cache doesn't report (e.g. a read-after-write race). That path called parseYaml unguarded. A note starting with --- whose YAML is malformed - for example:

---
status: : :
---
foo:: bar

gives Obsidian no usable frontmatter cache, so the live-parse path runs and parseYaml throws. The throw propagated out of parseFrontmatter -> controller.getPropertiesInFile() -> the Run MetaEdit command, aborting the parse for the whole note and dropping its inline/tag metadata. The post-merge Codex review on #130 flagged exactly this.

Fix

Wrap the live parseYaml call in try/catch and treat malformed YAML as non-parseable (return null), mirroring the bulk preflight's BulkMetadataEditor.readLiveFrontmatter, which already catches the identical failure. Returning null (rather than []) preserves the existing cache fallback chain, so a note that is only momentarily unparseable mid-edit still surfaces its last-known-good cached frontmatter instead of dropping it - consistent with how parseFrontmatter already degrades when there is no live frontmatter range.

Tests

  • Unit (src/parser.test.ts): asserts parseFrontmatter swallows a thrown parseYaml and resolves to []; that it falls back to cached frontmatter when the live parse throws; and that inline fields still surface (incl. CRLF). The shared obsidian test stub's parseYaml is a permissive line parser that never throws, so the throw is simulated with vi.spyOn.
  • Live E2E (tests/e2e/metaedit-runtime.test.ts): reproduces the real Obsidian throw with status: : : and asserts getPropertiesInFile returns the inline foo:: bar and the #mytag tag with no runtime errors. Polls the metadata cache for the tag to avoid an indexing-timing flake.

Verification

Reproduced live first in an isolated Obsidian E2E vault (Obsidian 1.x): pre-fix getPropertiesInFile threw Nested mappings are not allowed in compact mappings; post-fix it returns [#mytag, foo:: bar] and the Run MetaEdit command opens the suggester without error. Live edge-case sweep (empty frontmatter, only-malformed-key, bad indentation, tab indentation, CRLF, and a valid-frontmatter control) all pass.

Commands run: pnpm run lint (0 errors), pnpm run build, pnpm run test (178 passed), full tests/e2e suite (40 passed).

Refs #130

🤖 Generated with Claude Code

PR #130 added a live-parse fallback in parseFrontmatterContent for notes
whose frontmatter range Obsidian's cache doesn't report, but it called
parseYaml unguarded. A note starting with --- whose YAML is malformed
(e.g. 'status: : :') therefore threw, aborting controller.getPropertiesInFile()
for the whole note and dropping its inline and tag metadata.

Wrap the live parse in try/catch and treat malformed YAML as non-parseable
(return null), mirroring the bulk preflight's BulkMetadataEditor.readLiveFrontmatter.
Returning null preserves the existing cache fallback, so a note that is only
momentarily unparseable mid-edit still surfaces its last-known-good cached
frontmatter rather than dropping it.

Adds a unit regression (parseYaml mocked to throw, since the test stub's
parseYaml is permissive) and a live Obsidian E2E regression that reproduces
the real throw and asserts inline and tag metadata still surface.
@chhoumann
chhoumann merged commit b835f97 into master Jun 27, 2026
8 checks passed
github-actions Bot pushed a commit that referenced this pull request Jul 1, 2026
# [1.9.0](1.8.4...1.9.0) (2026-07-01)

### Bug Fixes

* address review findings from PRs [#119](https://github.com/chhoumann/MetaEdit/issues/119)-[#128](https://github.com/chhoumann/MetaEdit/issues/128) ([adf0b57](adf0b57))
* **api:** preserve reserved keys when cloning values ([#162](#162)) ([cff6076](cff6076))
* **automators:** count only [x]/[X] tasks complete; null-guard Kanban lane display ([#145](#145)) ([21da53a](21da53a))
* **bulk:** reject __proto__/constructor as a bulk property key ([#148](#148)) ([f03eacc](f03eacc))
* **bulk:** serialize bulk frontmatter writes through the controller write queue ([#147](#147)) ([3eb8236](3eb8236))
* **controller:** guard __proto__/constructor in all frontmatter writes ([#159](#159)) ([9d99fae](9d99fae))
* **controller:** make inline-field writes fence-aware ([5f8e713](5f8e713))
* **core,api:** safe command no-op, block-list deletion, presence-based property lookups ([#143](#143)) ([1702d08](1702d08))
* edit YAML lists (incl. tags) as native lists instead of collapsing them to a string ([#94](#94)) ([#128](#128)) ([79f6ee0](79f6ee0)), closes [#51](#51) [#36](#36)
* harden frontmatter metadata writes ([add2e90](add2e90))
* Kanban helper syncs only the card's leading link, not trailing date/reference links ([#126](#126)) ([52aec1e](52aec1e))
* **kanban:** don't write to an ambiguous same-named note ([#158](#158)) ([7f9655b](7f9655b))
* **kanban:** single notice for a card missing the board property ([9b1c88a](9b1c88a))
* parse inline fields behind list/quote markers and brackets ([#122](#122)) ([a62ef21](a62ef21)), closes [#119](#119) [#18](#18) [#121](#121) [#78](#78) [#84](#84)
* **parser:** preserve [[wikilinks]] in multi-value inline edits ([c331adb](c331adb))
* **settings:** serialize Auto Property choice persistence to prevent lost updates ([#154](#154)) ([2066b3e](2066b3e))
* **settings:** settings tab no longer clobbers concurrently-added Auto Property choices ([5759534](5759534))
* **settings:** validate auto-properties before queueing and make rollbacks compare-and-restore ([#156](#156)) ([8cbacdc](8cbacdc)), closes [#154](#154)
* stop inline-field updates appending a stray bracket ([#127](#127)) ([d2617f5](d2617f5)), closes [#67](#67) [#121](#121)
* **suggester:** center row action icons flush-right ([#166](#166)) ([c00795e](c00795e))
* **suggester:** use native icons + tooltips for row actions ([70903cc](70903cc))
* **suggester:** well-formed duplicate Notice, robust modal close, cleaner name suggestions ([#144](#144)) ([915dd6b](915dd6b)), closes [#143](#143)
* **tags:** correct and clarify tag editing ([#142](#142)) ([b837899](b837899)), closes [#49](#49)
* tolerate malformed YAML frontmatter when parsing note metadata ([#132](#132)) ([b835f97](b835f97)), closes [#130](#130)

### Features

* adopt modern Obsidian APIs and raise baseline ([5131664](5131664))
* **api:** append Dataview field instances without replacing ([42c78a8](42c78a8)), closes [#91](#91)
* **api:** expand public integration surface ([#120](#120)) ([bbf0a7a](bbf0a7a))
* assist property-value entry with autocomplete and a native date picker ([#125](#125)) ([cd86235](cd86235)), closes [#61](#61) [#74](#74) [#61](#61) [#61](#61)
* **auto-properties:** description, multi-select, and learn-as-you-go values ([#123](#123)) ([4b24296](4b24296)), closes [#59](#59) [#40](#40) [#59](#59) [#40](#40) [#59](#59) [#40](#40) [#43](#43) [#30](#30)
* **auto-properties:** paste a list to split into choices ([#139](#139)) ([0245f7a](0245f7a))
* bulk edit metadata across folders and selected notes ([#124](#124)) ([c8d91e2](c8d91e2)), closes [#64](#64) [#20](#20)
* **create:** fluid, type-aware native YAML property creation ([#170](#170)) ([59b4c5d](59b4c5d))
* **editor:** edit properties with Obsidian's native widgets; raise minAppVersion to 1.12.7 ([#168](#168)) ([73b37e0](73b37e0))
* hide file tags from the Edit Meta menu ([#46](#46), [#90](#90)) ([#131](#131)) ([2c07937](2c07937)), closes [#tag](https://github.com/chhoumann/MetaEdit/issues/tag) [#tags](https://github.com/chhoumann/MetaEdit/issues/tags)
* support nested YAML path editing ([e9fce98](e9fce98))
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 1.9.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant