{"record":{"id":"9f52c8d8335bc243","repo":"bmad-code-org/BMAD-METHOD","slug":"failed-to-read-render-source-path-error","errorCode":null,"errorMessage":"failed to read render source {path}: {error}","messagePattern":"failed to read render source (.+?): (.+?)","errorType":"exception","errorClass":"RenderError","httpStatus":null,"severity":"error","filePath":"src/scripts/render_skill.py","lineNumber":112,"sourceCode":"        result.append(layer)\n    return result\n\n\ndef _load_sources(skill_dir: Path) -> dict[str, str]:\n    sources: dict[str, str] = {}\n    for candidate in sorted(skill_dir.rglob(\"*.md\")):\n        if candidate.name == \"SKILL.md\":\n            continue\n        name = candidate.relative_to(skill_dir).as_posix()\n        path = candidate.resolve(strict=True)\n        if not path.is_relative_to(skill_dir):\n            raise RenderError(f\"render source escapes skill directory: {name}\")\n        if not path.is_file():\n            raise RenderError(f\"render source is missing or not a file: {path}\")\n        try:\n            sources[name] = path.read_text(encoding=\"utf-8\")\n        except (OSError, UnicodeError) as error:\n            raise RenderError(f\"failed to read render source {path}: {error}\") from error\n    if \"workflow.md\" not in sources:\n        raise RenderError(f\"render entry is missing: {skill_dir / 'workflow.md'}\")\n    return sources\n\n\ndef _resolve_config_value(value: Any, label: str, project_root: Path) -> str:\n    text = _require_string(value, label)\n    if \"{project-root}\" not in text:\n        return text\n    resolved = text.replace(\"{project-root}\", str(project_root))\n    if not Path(resolved).is_absolute():\n        raise RenderError(f\"{label} must resolve to an absolute path: {resolved}\")\n    return resolved\n\n\ndef _find_config_values(data: Any, key: str, prefix: str = \"\") -> list[tuple[str, Any]]:\n    matches: list[tuple[str, Any]] = []\n    if not isinstance(data, dict):","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/bmad-code-org/BMAD-METHOD/blob/b70486b9bdcb0a404d329e2a763b57964e7f1360/src/scripts/render_skill.py#L94-L130","documentation":"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.","triggerScenarios":"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.","commonSituations":"Non-UTF-8 markdown from legacy systems; permission errors in CI; another process deleting sources during render.","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."],"exampleFix":"# before: file saved as ISO-8859-1 (invalid UTF-8 bytes)\n# after: convert to UTF-8\niconv -f ISO-8859-1 -t UTF-8 notes.md -o notes.utf8.md && mv notes.utf8.md notes.md","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef assert_sources_readable_utf8(skill_dir: Path) -> None:\n    for cand in skill_dir.rglob(\"*.md\"):\n        if cand.name == \"SKILL.md\":\n            continue\n        try:\n            cand.resolve(strict=True).read_text(encoding=\"utf-8\")\n        except (OSError, UnicodeError) as e:\n            raise SystemExit(f\"cannot read {cand}: {e}\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Save all markdown as UTF-8.","Convert legacy encodings with iconv before rendering.","Ensure the runner has read permission on the skill tree."],"tags":["python","filesystem","encoding","utf-8","skill"],"backgroundTag":null,"analyzedSha":"b70486b9bdcb0a404d329e2a763b57964e7f1360","analyzedAt":"2026-08-13T01:21:12.247Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}