bmad-code-org/BMAD-METHOD · critical · RenderError

generation output hash mismatch: {destination / name}

Error message

generation output hash mismatch: {destination / name}

What it means

The SHA-256 of an existing output file does not match the hash recorded in manifest.json. The file's content was altered after generation, breaking the immutability guarantee, so the renderer refuses to proceed rather than silently accept tampered output.

Source

Thrown at src/scripts/render_skill.py:292

    except (OSError, UnicodeError, json.JSONDecodeError) as error:
        raise RenderError(f"corrupt existing generation {destination}: {error}") from error
    if existing != manifest:
        raise RenderError(f"generation collision or corruption at {destination}")
    expected_files = set(manifest["outputs"]) | {"manifest.json"}
    actual_files = {
        path.relative_to(destination).as_posix()
        for path in destination.rglob("*")
        if path.is_file()
    }
    if actual_files != expected_files:
        raise RenderError(f"generation contains unexpected or missing files: {destination}")
    for name, expected_hash in manifest["outputs"].items():
        try:
            actual_hash = _hash_bytes((destination / name).read_bytes())
        except OSError as error:
            raise RenderError(f"failed to verify {destination / name}: {error}") from error
        if actual_hash != expected_hash:
            raise RenderError(f"generation output hash mismatch: {destination / name}")


def _publish(destination: Path, outputs: dict[str, bytes], manifest: dict[str, Any]) -> None:
    destination.parent.mkdir(parents=True, exist_ok=True)
    if destination.exists():
        _verify_existing(destination, manifest)
        return
    staging = Path(tempfile.mkdtemp(prefix=".staging-", dir=destination.parent))
    try:
        for name, content in outputs.items():
            path = staging / name
            path.parent.mkdir(parents=True, exist_ok=True)
            path.write_bytes(content)
        (staging / "manifest.json").write_bytes(
            json.dumps(manifest, ensure_ascii=False, indent=2, sort_keys=True).encode("utf-8")
            + b"\n"
        )
        try:

View on GitHub (pinned to b70486b9bd)

Solutions

  1. Delete the modified generation directory and re-render to regenerate correct hashes.
  2. Prevent formatters/linters/editors from touching generated dirs (add to ignore lists).
  3. Normalize line endings via .gitattributes (* text=auto eol=lf) for cross-platform consistency.

Example fix

# before: hand-edited workflow.md changes its bytes
# after
rm -rf _bmad/render/<skill>/<slug>-<hash>
python src/scripts/render_skill.py --project-root . --skill skills/my-skill
Defensive patterns

Strategy: try-catch

Validate before calling

import hashlib, json
from pathlib import Path

def verify_hashes(dest: Path) -> None:
    manifest = json.loads((dest / "manifest.json").read_text(encoding="utf-8"))
    for name, expected in manifest["outputs"].items():
        actual = hashlib.sha256((dest / name).read_bytes()).hexdigest()
        if actual != expected:
            raise SystemExit(f"hash mismatch: {dest / name}")

Try / catch

from render_skill import render, RenderError

try:
    entry = render(project_root, skill_dir)
except RenderError as e:
    if "output hash mismatch" in str(e):
        # a generated file was tampered with -- delete the dir and regenerate
        ...
    raise

Prevention

When it happens

Trigger: A file under an existing generation directory was edited by hand (or rewritten by a formatter/linter) so its bytes no longer match the manifest hash.

Common situations: Accidentally editing a rendered file; a tool rewriting line endings; VCS checkout on a different OS converting CRLF/LF.

Related errors


AI-assisted analysis of bmad-code-org/BMAD-METHOD@b70486b9bd (2026-08-13). Data as JSON: /api/errors/4a733e12c442add7. Report an issue: GitHub.