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

  1. Search central config for the key (grep -rn project_name _bmad/) and if absent, add it to _bmad/config.toml.
  2. Correct the token spelling in the source to match an existing config key.
  3. 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

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


AI-assisted analysis of bmad-code-org/BMAD-METHOD@b70486b9bd (2026-08-13). Data as JSON: /api/errors/4861a53545ab7944. Report an issue: GitHub.