bmad-code-org/BMAD-METHOD · error · RenderError
{label} must resolve to an absolute path: {resolved}
Error message
{label} must resolve to an absolute path: {resolved} What it means
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.
Source
Thrown at src/scripts/render_skill.py:124
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):
return matches
for name, value in data.items():
path = f"{prefix}.{name}" if prefix else name
if name == key and not isinstance(value, (dict, list)):
matches.append((path, value))
matches.extend(_find_config_values(value, key, path))
return matches
def _resolve_short_config(
central: dict[str, Any], key: str, project_root: Path
) -> tuple[str, str]:View on GitHub (pinned to b70486b9bd)
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.
Example fix
# before
[paths]
output = " {project-root}/_bmad/render"
# after
[paths]
output = "{project-root}/_bmad/render" Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
def assert_absolute_after_substitution(text: str, project_root: Path) -> None:
if "{project-root}" in text:
resolved = text.replace("{project-root}", str(project_root))
if not Path(resolved).is_absolute():
raise SystemExit(f"value does not resolve to absolute path: {resolved!r}") Prevention
- 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.
When it happens
Trigger: 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.
Common situations: Accidental leading/trailing whitespace in a TOML string; a relative segment accidentally prepended; copy-pasting a path template.
Related errors
- missing config value `{key}`
- ambiguous config value `{key}` found at: {paths}
- keyed array identifier `{candidate}` must not be empty
- {label} must be a list of tables
- {item_label} must be a table
AI-assisted analysis of bmad-code-org/BMAD-METHOD@b70486b9bd (2026-08-13).
Data as JSON: /api/errors/7f7f57e73a859491.
Report an issue: GitHub.