bmad-code-org/BMAD-METHOD · error · RenderError
missing {label} `{dotted_path}`
Error message
missing {label} `{dotted_path}` What it means
render_skill.py's `_lookup` walks a dotted path (e.g. `workflow.open_spec`, `project.name`) through a config or customization dict; if any segment is missing or a non-leaf resolves to a non-dict, it raises `RenderError` naming the label and full path. It is invoked when a `{{config.foo.bar}}` or `{workflow.foo.bar}` token in a skill's markdown sources references a value that the merged config/customization layers do not provide. The render refuses to publish a snapshot with an unresolved token rather than emit a blank or `None`.
Source
Thrown at src/scripts/render_skill.py:50
_CUSTOM_TOKEN = re.compile(r"\{workflow\.([A-Za-z0-9_.-]+)\}")
_SNAPSHOT_TOKEN = re.compile(r"\[\[bmad-snapshot:([A-Za-z0-9_./-]+\.md)\]\]")
def _hash_bytes(content: bytes) -> str:
return hashlib.sha256(content).hexdigest()
def _canonical_json(value: Any) -> bytes:
return json.dumps(
value, ensure_ascii=False, sort_keys=True, separators=(",", ":")
).encode("utf-8")
def _lookup(data: dict[str, Any], dotted_path: str, label: str) -> Any:
current: Any = data
for part in dotted_path.split("."):
if not isinstance(current, dict) or part not in current:
raise RenderError(f"missing {label} `{dotted_path}`")
current = current[part]
return current
def _require_string(value: Any, label: str, *, allow_empty: bool = False) -> str:
if not isinstance(value, str):
raise RenderError(f"{label} must be a string, got {type(value).__name__}")
if not allow_empty and not value.strip():
raise RenderError(f"{label} must not be empty")
return value
def _require_string_list(value: Any, label: str) -> list[str]:
if not isinstance(value, list):
raise RenderError(f"{label} must be a list, got {type(value).__name__}")
result = []
for index, item in enumerate(value):
result.append(_require_string(item, f"{label}[{index}]"))View on GitHub (pinned to b70486b9bd)
Solutions
- Add the missing value at the reported path in the relevant TOML layer (`_bmad/config.toml` for config tokens, `customize.toml` for workflow tokens).
- Check for typos by comparing the token path against the TOML key spelling.
- If the value should be optional, remove the token from the markdown source instead of leaving it dangling.
- Run render with verbose output to list all tokens and confirm each has a source.
Example fix
# before: token {{config.project.codename}} in workflow.md, config.toml has
[project]
name = "demo"
# after: add the key
[project]
name = "demo"
codename = "atlas" Defensive patterns
Strategy: validation
Validate before calling
def path_present(data, dotted):
cur = data
for part in dotted.split('.'):
if not isinstance(cur, dict) or part not in cur: return False
cur = cur[part]
return True
for tok in tokens:
assert path_present(config, tok), f'missing config value `{tok}`' Type guard
def has_path(data: dict, dotted: str) -> bool:
cur = data
for part in dotted.split('.'):
if not isinstance(cur, dict) or part not in cur: return False
cur = cur[part]
return True Try / catch
from render_skill import RenderError
try:
_lookup(central, path, 'config value')
except RenderError as e:
print(f"error: {e}", file=sys.stderr); sys.exit(2) Prevention
- Add the config value before authoring a token that references it.
- When renaming a config key, grep the markdown sources for tokens and update them together.
- Run render in CI to catch unresolved tokens early.
When it happens
Trigger: A markdown source contains `{{config.project.codename}}` but `_bmad/config.toml` has no `codename` under `[project]`; a `{workflow.open_spec}` token but `customize.toml` lacks that key and no default ships; a typo in the token (`proejct`); the central config file was deleted so the whole subtree is absent.
Common situations: Authoring a new token before adding the config value; renaming a config key without updating tokens; disabling a config layer (e.g. removing `config.user.toml`) that previously supplied the value.
Related errors
- {label} must be a string, got {type(value).__name__}
- {label} must not be empty
- {label} must be a list, got {type(value).__name__}
- required TOML file not found: {path}
- TOML layer is not a file: {path}
AI-assisted analysis of bmad-code-org/BMAD-METHOD@b70486b9bd (2026-08-13).
Data as JSON: /api/errors/4097c0c28ff42826.
Report an issue: GitHub.