Skip to content

Commit 32236cd

Browse files
phernandezclaude
andauthored
fix: Handle YAML parsing errors gracefully in update_frontmatter (#378) (#379)
Signed-off-by: phernandez <paul@basicmachines.co> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 4fd6d0c commit 32236cd

2 files changed

Lines changed: 50 additions & 8 deletions

File tree

src/basic_memory/file_utils.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -210,11 +210,20 @@ async def update_frontmatter(path: FilePath, updates: Dict[str, Any]) -> str:
210210
# Read current content
211211
content = path_obj.read_text(encoding="utf-8")
212212

213-
# Parse current frontmatter
213+
# Parse current frontmatter with proper error handling for malformed YAML
214214
current_fm = {}
215215
if has_frontmatter(content):
216-
current_fm = parse_frontmatter(content)
217-
content = remove_frontmatter(content)
216+
try:
217+
current_fm = parse_frontmatter(content)
218+
content = remove_frontmatter(content)
219+
except (ParseError, yaml.YAMLError) as e:
220+
# Log warning and treat as plain markdown without frontmatter
221+
logger.warning(
222+
f"Failed to parse YAML frontmatter in {path_obj}: {e}. "
223+
"Treating file as plain markdown without frontmatter."
224+
)
225+
# Keep full content, treat as having no frontmatter
226+
current_fm = {}
218227

219228
# Update frontmatter
220229
new_fm = {**current_fm, **updates}
@@ -229,11 +238,13 @@ async def update_frontmatter(path: FilePath, updates: Dict[str, Any]) -> str:
229238
return await compute_checksum(final_content)
230239

231240
except Exception as e: # pragma: no cover
232-
logger.error(
233-
"Failed to update frontmatter",
234-
path=str(path) if isinstance(path, (str, Path)) else "<unknown>",
235-
error=str(e),
236-
)
241+
# Only log real errors (not YAML parsing, which is handled above)
242+
if not isinstance(e, (ParseError, yaml.YAMLError)):
243+
logger.error(
244+
"Failed to update frontmatter",
245+
path=str(path) if isinstance(path, (str, Path)) else "<unknown>",
246+
error=str(e),
247+
)
237248
raise FileError(f"Failed to update frontmatter: {e}")
238249

239250

tests/utils/test_file_utils.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,37 @@ async def test_update_frontmatter_errors(tmp_path: Path):
259259
await update_frontmatter(nonexistent, {"title": "Test"})
260260

261261

262+
@pytest.mark.asyncio
263+
async def test_update_frontmatter_handles_malformed_yaml(tmp_path: Path):
264+
"""Test that update_frontmatter handles malformed YAML gracefully (issue #378)."""
265+
test_file = tmp_path / "test.md"
266+
267+
# Create file with malformed YAML frontmatter (colon in title breaks YAML)
268+
content = """---
269+
title: KB: Something
270+
---
271+
272+
# Test Content
273+
274+
Some content here"""
275+
test_file.write_text(content)
276+
277+
# Should handle gracefully and treat as having no frontmatter
278+
updates = {"title": "Fixed Title", "type": "note"}
279+
await update_frontmatter(test_file, updates)
280+
281+
# Verify file was updated successfully
282+
updated = test_file.read_text(encoding="utf-8")
283+
assert "title: Fixed Title" in updated
284+
assert "type: note" in updated
285+
assert "Test Content" in updated
286+
assert "Some content here" in updated
287+
288+
# Verify new frontmatter is valid
289+
fm = parse_frontmatter(updated)
290+
assert fm == updates
291+
292+
262293
@pytest.mark.asyncio
263294
def test_sanitize_for_filename_removes_invalid_characters():
264295
# Test all invalid characters listed in the regex

0 commit comments

Comments
 (0)