bmad-code-org/BMAD-METHOD · error · RenderError

ambiguous config value `{key}` found at: {paths}

Error message

ambiguous config value `{key}` found at: {paths}

What it means

The short-config resolver requires exactly one match for {{.key}} across the whole central config tree. If the same terminal key name appears in multiple sections (e.g. project.name and team.name), it cannot choose deterministically and aborts, listing every location found.

Source

Thrown at src/scripts/render_skill.py:148

    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)


def _format_review_layers(layers: list[dict[str, str]]) -> str:
    active = [layer for layer in layers if layer["instruction"].strip()]
    if not active:

View on GitHub (pinned to b70486b9bd)

Solutions

  1. Switch the token to the explicit dotted form {{config.section.name}}.
  2. Rename one of the colliding keys so only one match remains.
  3. Use the full dotted paths reported in {paths} to disambiguate.

Example fix

# before: {{.name}} is ambiguous between project.name and team.name
# after: use the explicit path
{{config.project.name}}
Defensive patterns

Strategy: validation

Validate before calling

import re, tomllib
from pathlib import Path

SHORT = re.compile(r"\{\{\.([A-Za-z0-9_]+)\}\}")

def find_keys(obj, prefix="") -> list[str]:
    out: list[str] = []
    if isinstance(obj, dict):
        for k, v in obj.items():
            path = f"{prefix}.{k}" if prefix else k
            if not isinstance(v, (dict, list)):
                out.append(path)
            out += find_keys(v, path)
    return out

def validate_short_tokens_unambiguous(sources: dict[str,str], central_path: Path) -> None:
    central = tomllib.loads(Path(central_path).read_text(encoding="utf-8"))
    all_paths = find_keys(central)
    for txt in sources.values():
        for m in SHORT.finditer(txt):
            key = m.group(1)
            hits = [p for p in all_paths if p.split(".")[-1] == key]
            if len(hits) > 1:
                raise SystemExit(f"ambiguous {key}: {hits}")

Prevention

When it happens

Trigger: Source uses {{.name}}; central config contains both [project] name=... and [team] name=... . _find_config_values returns two matches, so _resolve_short_config raises with the dotted paths joined by commas.

Common situations: Reusing common key names (name, id, version, path) across multiple TOML sections; merging several config layers that each define a same-named leaf.

Related errors


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