bmad-code-org/BMAD-METHOD · error · RenderError
failed to verify
Error message
failed to verify {destination / name}: {error} What it means
During hash verification, reading an output file's bytes raised OSError (permission denied, file vanished between listing and read). The renderer reports which output file failed.
Solutions
- Fix read permissions on the named file (chmod 644).
- Ensure no concurrent process touches the generation directory during render.
- Regenerate the directory if the file is gone.
Example fix
# before: output file mode 000 (unreadable) # after chmod 644 _bmad/render/<skill>/<slug>-<hash>/workflow.md
Defensive patterns
Strategy: try-catch
Validate before calling
from pathlib import Path
def ensure_outputs_readable(dest: Path, names: list[str]) -> None:
for name in names:
p = dest / name
try:
p.read_bytes()
except OSError as e:
raise SystemExit(f"unreadable {p}: {e}") Try / catch
from render_skill import render, RenderError
try:
entry = render(project_root, skill_dir)
except RenderError as e:
if "failed to verify" in str(e):
# fix permissions / remove blockers on the named file, then retry
...
raise Prevention
- Keep generation files world-readable.
- Prevent concurrent processes from mutating generation dirs.
- On verification failure, regenerate the directory.
When it happens
Trigger: A file listed in the manifest became unreadable during the verification loop -- permissions changed, or a race with another process removed the file after the directory listing.
Common situations: Permission changes between run stages; concurrent modification of the generation dir; flaky or networked filesystems.
Related errors
- generation contains unexpected or missing files
- corrupt existing generation
- generation output hash mismatch
- failed to read
- failed to read render source
AI-assisted analysis of bmad-code-org/BMAD-METHOD@b70486b9bd (2026-08-13).
Data as JSON: /api/errors/468ccca82b2f4d02.
Report an issue: GitHub.
Appendix: source
Thrown at src/scripts/render_skill.py:290
try:
existing = json.loads(manifest_path.read_text(encoding="utf-8"))
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"View on GitHub (pinned to b70486b9bd)