bmad-code-org/BMAD-METHOD · error · RenderError
failed to read render source {path}: {error}
Error message
failed to read render source {path}: {error} What it means
Wraps path.read_text(encoding="utf-8") errors (OSError, UnicodeError) so render failures are uniform. Fires on I/O errors (permissions, disk failure, file vanished mid-run) or when the file's bytes are not valid UTF-8.
Source
Thrown at src/scripts/render_skill.py:112
result.append(layer)
return result
def _load_sources(skill_dir: Path) -> dict[str, str]:
sources: dict[str, str] = {}
for candidate in sorted(skill_dir.rglob("*.md")):
if candidate.name == "SKILL.md":
continue
name = candidate.relative_to(skill_dir).as_posix()
path = candidate.resolve(strict=True)
if not path.is_relative_to(skill_dir):
raise RenderError(f"render source escapes skill directory: {name}")
if not path.is_file():
raise RenderError(f"render source is missing or not a file: {path}")
try:
sources[name] = path.read_text(encoding="utf-8")
except (OSError, UnicodeError) as error:
raise RenderError(f"failed to read render source {path}: {error}") from error
if "workflow.md" not in sources:
raise RenderError(f"render entry is missing: {skill_dir / 'workflow.md'}")
return sources
def _resolve_config_value(value: Any, label: str, project_root: Path) -> str:
text = _require_string(value, label)
if "{project-root}" not in text:
return text
resolved = text.replace("{project-root}", str(project_root))
if not Path(resolved).is_absolute():
raise RenderError(f"{label} must resolve to an absolute path: {resolved}")
return resolved
def _find_config_values(data: Any, key: str, prefix: str = "") -> list[tuple[str, Any]]:
matches: list[tuple[str, Any]] = []
if not isinstance(data, dict):View on GitHub (pinned to b70486b9bd)
Solutions
- Re-encode the file as UTF-8: iconv -f latin-1 -t utf-8 file.md -o file.utf8.md && mv file.utf8.md file.md.
- Fix filesystem permissions so the runner can read the file.
- Ensure no external process mutates or deletes sources while the renderer runs.
Example fix
# before: file saved as ISO-8859-1 (invalid UTF-8 bytes) # after: convert to UTF-8 iconv -f ISO-8859-1 -t UTF-8 notes.md -o notes.utf8.md && mv notes.utf8.md notes.md
Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
def assert_sources_readable_utf8(skill_dir: Path) -> None:
for cand in skill_dir.rglob("*.md"):
if cand.name == "SKILL.md":
continue
try:
cand.resolve(strict=True).read_text(encoding="utf-8")
except (OSError, UnicodeError) as e:
raise SystemExit(f"cannot read {cand}: {e}") Prevention
- Save all markdown as UTF-8.
- Convert legacy encodings with iconv before rendering.
- Ensure the runner has read permission on the skill tree.
When it happens
Trigger: File becomes unreadable between the rglob and the read (permission change, deletion), or contains bytes that are invalid UTF-8 (e.g. legacy latin-1/cp1252 markdown). The (OSError, UnicodeError) except clause re-raises as RenderError.
Common situations: Non-UTF-8 markdown from legacy systems; permission errors in CI; another process deleting sources during render.
Related errors
- render source escapes skill directory: {name}
- render source is missing or not a file: {path}
- render entry is missing: {skill_dir / 'workflow.md'}
- corrupt existing generation {destination}: {error}
- generation contains unexpected or missing files: {destinatio
AI-assisted analysis of bmad-code-org/BMAD-METHOD@b70486b9bd (2026-08-13).
Data as JSON: /api/errors/9f52c8d8335bc243.
Report an issue: GitHub.