Skip to content

update_frontmatter logs errors for malformed YAML instead of handling gracefully #378

@phernandez

Description

@phernandez

Summary

The update_frontmatter() function in file_utils.py logs level 17 errors when encountering malformed YAML frontmatter, even though the system should handle these gracefully.

Evidence from Production

Last 3 hours: 1,112 errors of "Failed to update frontmatter"

All errors are from files with colons in titles that confuse the YAML parser:

---
title: KB: Something  # The second colon breaks YAML
---

Error Pattern:

Failed to update frontmatter
mapping values are not allowed here in "<unicode string>", line 2, column 10

Affected Files: 22+ files with KB: prefix in titles

Root Cause

PR #368 fixed YAML error handling in entity parsing (reading files), but update_frontmatter() has a separate code path that wasn't fixed:

Location: src/basic_memory/file_utils.py:188-237

async def update_frontmatter(path: FilePath, updates: Dict[str, Any]) -> str:
    try:
        # ...
        if has_frontmatter(content):
            current_fm = parse_frontmatter(content)  # ← Raises ParseError for YAML errors
            # ...
    except Exception as e:  # pragma: no cover
        logger.error(  # ← Logs as ERROR (level 17)
            "Failed to update frontmatter",
            path=str(path),
            error=str(e),
        )
        raise FileError(f"Failed to update frontmatter: {e}")

Impact

  • Log Spam: 1,112 errors in 3 hours from same files retrying
  • Misleading Severity: Files are actually processed fine, errors are cosmetic
  • Monitoring: Real errors get buried in YAML parsing noise
  • User Confusion: Errors appear critical but have no actual impact

Expected Behavior

Should match PR #368's handling in entity parser:

  1. Catch YAML parsing errors gracefully
  2. Log WARNING (level 13) instead of ERROR
  3. Treat file as having no frontmatter
  4. Proceed with the update using new frontmatter

Proposed Fix

Update update_frontmatter() to handle YAML errors like entity_parser.py does:

async def update_frontmatter(path: FilePath, updates: Dict[str, Any]) -> str:
    try:
        path_obj = Path(path) if isinstance(path, str) else path
        content = path_obj.read_text(encoding="utf-8")
        
        # Parse current frontmatter with error handling
        current_fm = {}
        if has_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
                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}
        
        # Write new file with updated frontmatter
        yaml_fm = yaml.dump(new_fm, sort_keys=False, allow_unicode=True)
        final_content = f"---\n{yaml_fm}---\n\n{content.strip()}"
        
        await write_file_atomic(path_obj, final_content)
        return await compute_checksum(final_content)
        
    except Exception as e:
        # Only log real errors (not YAML parsing)
        if not isinstance(e, (ParseError, yaml.YAMLError)):
            logger.error(
                "Failed to update frontmatter",
                path=str(path),
                error=str(e),
            )
        raise FileError(f"Failed to update frontmatter: {e}")

Priority

P1 - High volume of error logs in production affecting monitoring and observability. Quick fix, same pattern as PR #368.

Related

Success Criteria

  1. YAML parsing errors logged as WARNING (level 13) not ERROR (level 17)
  2. Error count drops from 1,112/3hrs to ~0
  3. Files with malformed frontmatter still get updated successfully
  4. Real file operation errors still logged as ERROR

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions