bmad-code-org/BMAD-METHOD · error · RenderError
missing config value `{key}`
Error message
missing config value `{key}` What it means
Short-config tokens of the form {{.key}} are resolved by searching the entire central config tree for any field named key. If no field with that exact name exists anywhere in the merged central config (_bmad/config.toml plus user/custom layers), the renderer cannot resolve the token and aborts. This is distinct from the dotted {{config.a.b}} form which uses _lookup.
Source
Thrown at src/scripts/render_skill.py:145
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]:
matches = _find_config_values(central, key)
if not matches:
raise RenderError(f"missing config value `{key}`")
if len(matches) > 1:
paths = ", ".join(path for path, _ in matches)
raise RenderError(f"ambiguous config value `{key}` found at: {paths}")
path, value = matches[0]
return path, _resolve_config_value(value, f"config.{path}", project_root)
def _format_markdown_list(items: list[str]) -> str:
if not items:
return "_None._"
rendered = []
for item in items:
lines = item.splitlines() or [""]
rendered.append("- " + lines[0])
rendered.extend(" " + line for line in lines[1:])
return "\n".join(rendered)
View on GitHub (pinned to b70486b9bd)
Solutions
- Search central config for the key (grep -rn project_name _bmad/) and if absent, add it to _bmad/config.toml.
- Correct the token spelling in the source to match an existing config key.
- Switch to the explicit dotted form {{config.section.key}} for unambiguous resolution.
Example fix
# before: source has {{.proj_name}} but config defines `name`
# after (option A): fix the token
{{.name}}
# after (option B): add the key to _bmad/config.toml
[project]
proj_name = "my-app" Defensive patterns
Strategy: validation
Validate before calling
import re, tomllib
from pathlib import Path
SHORT = re.compile(r"\{\{\.([A-Za-z0-9_]+)\}\}")
def collect_keys(obj) -> set[str]:
keys: set[str] = set()
if isinstance(obj, dict):
for k, v in obj.items():
keys.add(k); keys |= collect_keys(v)
return keys
def validate_short_tokens(sources: dict[str,str], central_path: Path) -> None:
central = tomllib.loads(Path(central_path).read_text(encoding="utf-8"))
have = collect_keys(central)
for txt in sources.values():
for m in SHORT.finditer(txt):
if m.group(1) not in have:
raise SystemExit(f"missing config value `{m.group(1)}`") Prevention
- Grep central config for each {{.key}} token before rendering.
- Prefer the explicit {{config.section.key}} form for clarity.
- Keep a single source of truth for each short-config key.
When it happens
Trigger: A source contains {{.project_name}} but the merged central config has no key literally named project_name at any depth. _find_config_values returns an empty list, so _resolve_short_config raises.
Common situations: Typo in the token key; renamed config key; a missing config layer file; expecting a key from config.user.toml that was never created.
Related errors
- ambiguous config value `{key}` found at: {paths}
- {label} must resolve to an absolute path: {resolved}
- 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/4861a53545ab7944.
Report an issue: GitHub.