bmad-code-org/BMAD-METHOD · error · ValueError
.memlog.md has no frontmatter
Error message
.memlog.md has no frontmatter
What it means
memlog.py's `split` parses a `.memlog.md` file into frontmatter and body. It requires the very first line to be exactly `---`. This error means the file does not start with an opening fence — either the file is empty, the first line has trailing whitespace or a different dash count, or the file is a plain markdown body with no frontmatter block at all. The contract is documented at the top of the script: every memlog begins with a `---`-delimited header.
Source
Thrown at src/scripts/memlog.py:98
def now() -> str:
return datetime.now().strftime("%Y-%m-%dT%H:%M")
def resolve(args) -> Path:
"""The memlog file, from either addressing mode: {workspace}/.memlog.md or an explicit --path."""
return Path(args.path) if args.path else Path(args.workspace) / MEMLOG
def split(text: str) -> tuple[dict, str]:
"""Return (frontmatter dict in source order, body str). Frontmatter is plain key: value.
The closing fence is the first line that is *exactly* `---`, so a `---` inside a
field value (topic/goal are free user text) never truncates the frontmatter.
"""
lines = text.splitlines()
if not lines or lines[0] != "---":
raise ValueError(".memlog.md has no frontmatter")
end = next((i for i in range(1, len(lines)) if lines[i] == "---"), None)
if end is None:
raise ValueError(".memlog.md frontmatter is not terminated")
meta: dict[str, str] = {}
for line in lines[1:end]:
if ":" in line:
k, v = line.split(":", 1)
meta[k.strip()] = v.strip()
return meta, "\n".join(lines[end + 1:]).lstrip("\n")
def render(meta: dict, body: str) -> str:
# Neutralize newlines in values so a multi-line field can't break the fence on re-read.
fm = "\n".join(f"{k}: {' '.join(str(v).splitlines())}" for k, v in meta.items())
return "---\n" + fm + "\n---\n\n" + body.rstrip("\n") + "\n"
def touch(meta: dict) -> None:View on GitHub (pinned to b70486b9bd)
Solutions
- Prepend a proper frontmatter block starting with a line that is exactly three dashes.
- Use `memlog init --workspace <dir>` to create a correctly shaped file rather than hand-writing it.
- Check for trailing whitespace or CRLF on the first line and strip it.
- If the file should have no frontmatter, reconsider: memlog requires it; convert the body into entries via `append`.
Example fix
# before (.memlog.md) - (note) hello # after --- topic: demo goal: show the fix updated: 2026-08-12T10:00 --- - (note) hello
Defensive patterns
Strategy: validation
Validate before calling
text = Path('.memlog.md').read_text()
lines = text.splitlines()
assert lines and lines[0] == '---', '.memlog.md has no frontmatter' Type guard
def has_opening_fence(text: str) -> bool:
lines = text.splitlines()
return bool(lines) and lines[0] == '---' Try / catch
from memlog import split
try:
meta, body = split(text)
except ValueError as e:
print(f"error: {e}", file=sys.stderr); sys.exit(2) Prevention
- Always create memlogs with `memlog init`, never by hand.
- Ensure the first line is exactly three dashes with no trailing whitespace.
- Normalise line endings to LF to keep exact fence matching stable.
When it happens
Trigger: Resuming a session against a file that was hand-created without the fence; a file whose first line is `--- ` (trailing space) or `----` (four dashes); an empty file; a body-only log saved by an external editor that stripped the frontmatter.
Common situations: Manually seeding a memlog instead of using `init`; a find-replace that altered the fence; a sync tool that normalised line endings and broke the exact-match.
Related errors
- .memlog.md frontmatter is not terminated
- --extra must be a JSON array of objects
- each --extra entry must be a JSON object, got: {item!r}
- --extra must be a JSON array of objects
- each --extra entry must be a JSON object, got: {item!r}
AI-assisted analysis of bmad-code-org/BMAD-METHOD@b70486b9bd (2026-08-13).
Data as JSON: /api/errors/4266874f73b14528.
Report an issue: GitHub.