{"record":{"id":"7f7f57e73a859491","repo":"bmad-code-org/BMAD-METHOD","slug":"label-must-resolve-to-an-absolute-path-resolve","errorCode":null,"errorMessage":"{label} must resolve to an absolute path: {resolved}","messagePattern":"(.+?) must resolve to an absolute path: (.+?)","errorType":"exception","errorClass":"RenderError","httpStatus":null,"severity":"error","filePath":"src/scripts/render_skill.py","lineNumber":124,"sourceCode":"            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):\n        return matches\n    for name, value in data.items():\n        path = f\"{prefix}.{name}\" if prefix else name\n        if name == key and not isinstance(value, (dict, list)):\n            matches.append((path, value))\n        matches.extend(_find_config_values(value, key, path))\n    return matches\n\n\ndef _resolve_short_config(\n    central: dict[str, Any], key: str, project_root: Path\n) -> tuple[str, str]:","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/bmad-code-org/BMAD-METHOD/blob/b70486b9bdcb0a404d329e2a763b57964e7f1360/src/scripts/render_skill.py#L106-L142","documentation":"In _resolve_config_value, a config string containing the literal {project-root} is substituted with project_root and the result must be an absolute path. This fires when the substituted string is not absolute -- typically because of leading whitespace or a relative prefix before {project-root} in the TOML value, which makes Path(...).is_absolute() False despite the embedded absolute project root.","triggerScenarios":"A config value like output = \" {project-root}/_bmad/render\" (leading space) or output = \"rel/{project-root}\" (relative prefix) -- the leading text makes the whole path relative even though an absolute segment is embedded. Path(resolved).is_absolute() returns False.","commonSituations":"Accidental leading/trailing whitespace in a TOML string; a relative segment accidentally prepended; copy-pasting a path template.","solutions":["Ensure the config value starts with {project-root} with no leading whitespace or relative prefix.","Trim stray whitespace around the value in the TOML file.","Use a literal absolute path if {project-root} is not needed at the start of the string."],"exampleFix":"# before\n[paths]\noutput = \" {project-root}/_bmad/render\"\n\n# after\n[paths]\noutput = \"{project-root}/_bmad/render\"","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef assert_absolute_after_substitution(text: str, project_root: Path) -> None:\n    if \"{project-root}\" in text:\n        resolved = text.replace(\"{project-root}\", str(project_root))\n        if not Path(resolved).is_absolute():\n            raise SystemExit(f\"value does not resolve to absolute path: {resolved!r}\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Start {project-root} config values at the beginning of the string with no leading whitespace.","Avoid prepending relative segments before {project-root}.","Lint TOML string values for surrounding whitespace."],"tags":["python","config","toml","path","validation"],"backgroundTag":null,"analyzedSha":"b70486b9bdcb0a404d329e2a763b57964e7f1360","analyzedAt":"2026-08-13T01:21:12.247Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}