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>
This commit is contained in:
Paul Hernandez
2025-10-16 20:17:58 -05:00
committed by GitHub
parent 4fd6d0c648
commit 32236cd247
2 changed files with 50 additions and 8 deletions
+19 -8
View File
@@ -210,11 +210,20 @@ async def update_frontmatter(path: FilePath, updates: Dict[str, Any]) -> str:
# Read current content
content = path_obj.read_text(encoding="utf-8")
# Parse current frontmatter
# Parse current frontmatter with proper error handling for malformed YAML
current_fm = {}
if has_frontmatter(content):
current_fm = parse_frontmatter(content)
content = remove_frontmatter(content)
try:
current_fm = parse_frontmatter(content)
content = remove_frontmatter(content)
except (ParseError, yaml.YAMLError) as e:
# Log warning and treat as plain markdown without frontmatter
logger.warning(
f"Failed to parse YAML frontmatter in {path_obj}: {e}. "
"Treating file as plain markdown without frontmatter."
)
# Keep full content, treat as having no frontmatter
current_fm = {}
# Update frontmatter
new_fm = {**current_fm, **updates}
@@ -229,11 +238,13 @@ async def update_frontmatter(path: FilePath, updates: Dict[str, Any]) -> str:
return await compute_checksum(final_content)
except Exception as e: # pragma: no cover
logger.error(
"Failed to update frontmatter",
path=str(path) if isinstance(path, (str, Path)) else "<unknown>",
error=str(e),
)
# Only log real errors (not YAML parsing, which is handled above)
if not isinstance(e, (ParseError, yaml.YAMLError)):
logger.error(
"Failed to update frontmatter",
path=str(path) if isinstance(path, (str, Path)) else "<unknown>",
error=str(e),
)
raise FileError(f"Failed to update frontmatter: {e}")
+31
View File
@@ -259,6 +259,37 @@ async def test_update_frontmatter_errors(tmp_path: Path):
await update_frontmatter(nonexistent, {"title": "Test"})
@pytest.mark.asyncio
async def test_update_frontmatter_handles_malformed_yaml(tmp_path: Path):
"""Test that update_frontmatter handles malformed YAML gracefully (issue #378)."""
test_file = tmp_path / "test.md"
# Create file with malformed YAML frontmatter (colon in title breaks YAML)
content = """---
title: KB: Something
---
# Test Content
Some content here"""
test_file.write_text(content)
# Should handle gracefully and treat as having no frontmatter
updates = {"title": "Fixed Title", "type": "note"}
await update_frontmatter(test_file, updates)
# Verify file was updated successfully
updated = test_file.read_text(encoding="utf-8")
assert "title: Fixed Title" in updated
assert "type: note" in updated
assert "Test Content" in updated
assert "Some content here" in updated
# Verify new frontmatter is valid
fm = parse_frontmatter(updated)
assert fm == updates
@pytest.mark.asyncio
def test_sanitize_for_filename_removes_invalid_characters():
# Test all invalid characters listed in the regex