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

snapshot reference targets undeclared source: {target}

Error message

snapshot reference targets undeclared source: {target}

What it means

Tokens of the form [[bmad-snapshot:foo.md]] resolve to the absolute destination path of another rendered source file. The target must be one of the collected source files. If foo.md is not a real source (typo, missing file, wrong case, or path outside the skill dir), the renderer refuses to emit a dangling reference.

Source

Thrown at src/scripts/render_skill.py:260

        for token, value in replacements.items()
    }
    source_names = set(sources)
    patterns = [
        *(re.escape(token) for token in sorted(replacements, key=len, reverse=True)),
        _SNAPSHOT_TOKEN.pattern,
    ]
    token_pattern = re.compile("|".join(patterns))

    def replace(match: re.Match[str]) -> str:
        token = match.group(0)
        if token in replacements:
            return replacements[token]
        snapshot = _SNAPSHOT_TOKEN.fullmatch(token)
        if snapshot is None:
            raise RenderError(f"unsupported render token: {token}")
        target = snapshot.group(1)
        if target not in source_names:
            raise RenderError(f"snapshot reference targets undeclared source: {target}")
        return str(destination / target)

    rendered: dict[str, str] = {}
    for name, content in sources.items():
        # Inserted paths and customization prose are never scanned as source tokens.
        rendered[name] = token_pattern.sub(replace, content)
    return rendered


def _verify_existing(destination: Path, manifest: dict[str, Any]) -> None:
    manifest_path = destination / "manifest.json"
    try:
        existing = json.loads(manifest_path.read_text(encoding="utf-8"))
    except (OSError, UnicodeError, json.JSONDecodeError) as error:
        raise RenderError(f"corrupt existing generation {destination}: {error}") from error
    if existing != manifest:
        raise RenderError(f"generation collision or corruption at {destination}")
    expected_files = set(manifest["outputs"]) | {"manifest.json"}

View on GitHub (pinned to b70486b9bd)

Solutions

  1. Create the referenced .md file in the skill directory, or correct the token to match an existing source name.
  2. Check exact case and relative path (POSIX separators, relative to the skill root).
  3. Remove the snapshot token if the reference is no longer needed.

Example fix

# before: [[bmad-snapshot:Glossery.md]]  (typo, wrong case)
# after
[[bmad-snapshot:glossary.md]]
Defensive patterns

Strategy: validation

Validate before calling

import re
from pathlib import Path

SNAP = re.compile(r"\[\[bmad-snapshot:([A-Za-z0-9_./-]+\.md)\]\]")

def validate_snapshot_targets(skill_dir: Path) -> None:
    sources = {p.relative_to(skill_dir).as_posix() for p in skill_dir.rglob("*.md") if p.name != "SKILL.md"}
    for md in skill_dir.rglob("*.md"):
        if md.name == "SKILL.md":
            continue
        for m in SNAP.finditer(md.read_text(encoding="utf-8")):
            if m.group(1) not in sources:
                raise SystemExit(f"snapshot target missing: {m.group(1)}")

Prevention

When it happens

Trigger: A source contains [[bmad-snapshot:glossary.md]] but the skill directory has no glossary.md (only Glossary.md, or it was deleted, or the relative path is wrong). target not in source_names.

Common situations: Renaming a referenced file without updating snapshot tokens; case mismatch; pointing to a non-.md file or to a file outside the skill directory.

Related errors


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