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
- Create the referenced .md file in the skill directory, or correct the token to match an existing source name.
- Check exact case and relative path (POSIX separators, relative to the skill root).
- 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
- Keep snapshot targets in sync with source filenames (update tokens when renaming).
- Use exact case and POSIX relative paths in [[bmad-snapshot:...]] tokens.
- Add a pre-render lint for dangling snapshot references.
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
- missing config value `{key}`
- ambiguous config value `{key}` found at: {paths}
- {label} must be a list of tables
- {item_label} must be a table
- duplicate review layer id `{identifier}`
AI-assisted analysis of bmad-code-org/BMAD-METHOD@b70486b9bd (2026-08-13).
Data as JSON: /api/errors/24a63d00ebc295a0.
Report an issue: GitHub.