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

  1. Add the missing value at the reported path in the relevant TOML layer (`_bmad/config.toml` for config tokens, `customize.toml` for workflow tokens).
  2. Check for typos by comparing the token path against the TOML key spelling.
  3. If the value should be optional, remove the token from the markdown source instead of leaving it dangling.
  4. 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

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


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