{"record":{"id":"c6ae5e5edf9f46b3","repo":"bmad-code-org/BMAD-METHOD","slug":"render-source-is-missing-or-not-a-file-path","errorCode":null,"errorMessage":"render source is missing or not a file: {path}","messagePattern":"render source is missing or not a file: (.+?)","errorType":"exception","errorClass":"RenderError","httpStatus":null,"severity":"error","filePath":"src/scripts/render_skill.py","lineNumber":108,"sourceCode":"            ),\n        }\n        if \"when\" in item:\n            layer[\"when\"] = _require_string(item[\"when\"], f\"{item_label}.when\")\n        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","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/bmad-code-org/BMAD-METHOD/blob/b70486b9bdcb0a404d329e2a763b57964e7f1360/src/scripts/render_skill.py#L90-L126","documentation":"After confirming the resolved path is inside the skill dir, _load_sources checks path.is_file(). This fires when the path exists (resolve(strict=True) succeeded, so it is not missing) but is not a regular file -- typically a directory or a special file. rglob(\"*.md\") can match a directory literally named something.md.","triggerScenarios":"A directory named e.g. changelog.md inside the skill dir, or a FIFO/device file matching the *.md glob. resolve(strict=True) does not raise (the path exists), but is_file() is False.","commonSituations":"A directory accidentally named with a .md suffix; archive folders; special files on unusual filesystems.","solutions":["Remove or rename the non-file entry matching *.md so all matches are regular files.","Locate directory matches with: find <skill_dir> -name '*.md' -type d.","Ensure no FIFO/socket/device files are named *.md in the skill tree."],"exampleFix":"# before: a directory named changelog.md exists\nskills/my-skill/changelog.md/inner.md\n\n# after: rename the directory so it does not match *.md\nskills/my-skill/changelog/inner.md","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef assert_all_md_are_files(skill_dir: Path) -> None:\n    for cand in skill_dir.rglob(\"*.md\"):\n        if cand.name == \"SKILL.md\":\n            continue\n        real = cand.resolve(strict=True)\n        if not real.is_file():\n            raise SystemExit(f\"not a regular file: {real}\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Do not name directories with a .md suffix.","Check for directory matches: find <skill_dir> -name '*.md' -type d.","Keep skill sources as plain regular files."],"tags":["python","filesystem","validation","skill"],"backgroundTag":null,"analyzedSha":"b70486b9bdcb0a404d329e2a763b57964e7f1360","analyzedAt":"2026-08-13T01:21:12.247Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}