bmad-code-org/BMAD-METHOD · error · ValueError
{field} mismatch after write
Error message
{field} mismatch after write What it means
Companion to the development_status check: after the atomic write and re-read, the scalar header fields `generated`, `last_updated`, and `project` must match `str(doc[field])`. It fires when YAML coerces one of those scalars on re-read so its `str()` differs from the written value — most often because the field looks like a date or number and YAML returns a `datetime.date`/`int` object whose `str()` is not byte-identical to the original string. The exception handler restores the original file (or deletes it) and fails loud, so the file is never left half-mutated.
Source
Thrown at src/bmm-skills/plan/bmad-sprint-planning/scripts/sprint_plan.py:457
"warnings": warnings,
**report,
}
if args.dry_run:
print(json.dumps(result, default=str))
return
try:
payload = _dump_bytes(yaml, doc)
_atomic_write(args.status_file, payload, original_mode)
verify_yaml = _make_yaml()
with io.open(args.status_file, "r", encoding="utf-8") as fh:
reread = verify_yaml.load(fh)
if dict(reread.get("development_status") or {}) != {k: v for k, v in dev.items()}:
raise ValueError("development_status mismatch after write")
for field in ("generated", "last_updated", "project"):
if str(reread.get(field)) != str(doc[field]):
raise ValueError(f"{field} mismatch after write")
except Exception as exc:
if original_bytes is not None:
try:
_atomic_write(args.status_file, original_bytes, original_mode)
restored = True
except Exception:
restored = False
else:
Path(args.status_file).unlink(missing_ok=True)
restored = True
_fail(f"write or validation failed, original {'restored' if restored else 'NOT restored'}: {exc}",
restored=restored)
print(json.dumps(result, default=str))
def _parse_stamp(value):
from datetime import datetime
View on GitHub (pinned to b70486b9bd)
Solutions
- Confirm `--date`, `--project`, and any inherited `generated` value are plain strings and not bare numbers or full datetimes.
- If `generated` carries a time component from an older file, normalise it to a `YYYY-MM-DD` string at the source.
- Force the emitter to quote these three fields as explicit strings (a custom representer or `default_style='"'`) so the loader cannot coerce them.
- Reproduce with `--dry-run` (which skips the write) and then read the file back by hand to see which field's re-read type diverges.
Example fix
# before: last_updated stored as a bare date, loader returns datetime.date last_updated: 2026-08-12 # after: quote it so it stays a string last_updated: "2026-08-12" # or in code, coerce before writing: doc[field] = str(doc[field])
Defensive patterns
Strategy: validation
Validate before calling
def scalar_roundtrips(value, yaml_lib) -> bool:
dumped = yaml_lib.dump(str(value))
back = yaml_lib.load(dumped)
return str(back) == str(value)
fields = {f: doc[f] for f in ("generated","last_updated","project")}
bad = [f for f,v in fields.items() if not scalar_roundtrips(v, yaml)]
if bad:
raise SystemExit(f"fields would not round-trip: {bad}") Type guard
def is_iso_date_str(s: object) -> bool:
if not isinstance(s, str): return False
try:
datetime.strptime(s, "%Y-%m-%d"); return True
except ValueError:
return False Try / catch
# Treat non-zero exit as fatal; the handler has already restored the file.
import subprocess
res = subprocess.run(["python","sprint_plan.py",...], capture_output=True)
if res.returncode != 0:
# message reports restoration status; do NOT retry the same inputs
raise RuntimeError(res.stderr.decode()) Prevention
- Pass --date as a strict YYYY-MM-DD string, never a full datetime.
- Keep project names alphanumeric; quote anything that looks numeric in the source.
- Pin YAML library versions to keep date-coercion behaviour stable.
- Use --dry-run to validate the document before the write is attempted.
When it happens
Trigger: `generated`/`last_updated` is written as an ISO date string that the YAML loader turns into a `datetime.date`; `project` is a bare number or a YAML-reserved word. The check `str(reread.get(field)) != str(doc[field])` then trips because the re-read object's `str()` (e.g. `'2026-08-12'` from a date vs `'2026-08-12'` string is usually equal, but a `datetime` gives `'2026-08-12 00:00:00'`).
Common situations: Passing `--date 2026-08-12` where the loader returns a `datetime` whose repr adds time components; a project name that is purely numeric; a version bump of the YAML library that newly date-parses a previously-opaque string.
Related errors
- development_status mismatch after write
- All selected tool(s) are suspended: ${suspendedIdes.join(',
- Platform codes configuration not found at: ${PLATFORM_CODES_
- Invalid GITHUB_REPOSITORY format: "${process.env.GITHUB_REPO
- --extra must be a JSON array of objects
AI-assisted analysis of bmad-code-org/BMAD-METHOD@b70486b9bd (2026-08-13).
Data as JSON: /api/errors/d57516af8bd995af.
Report an issue: GitHub.