github/spec-kit · error · TemplateResolutionError
Unknown template composition strategy '{strategy}' in {path}
Error message
Unknown template composition strategy '{strategy}' in {path} What it means
Template layer composition only understands the strategies prepend, append, wrap, and replace (replace layers become the base). A layer manifest whose strategy string is anything else hits the final else-branch in compose_from_base() and raises TemplateResolutionError naming the unknown strategy and the layer file that declared it.
Source
Thrown at scripts/python/common.py:469
def compose_from_base() -> str:
try:
content = layers[-1][0].read_bytes().decode("utf-8")
for path, strategy in reversed(layers[:-1]):
layer_content = path.read_bytes().decode("utf-8")
if strategy == "prepend":
content = f"{layer_content}\n\n{content}"
elif strategy == "append":
content = f"{content}\n\n{layer_content}"
elif strategy == "wrap":
placeholder = "{CORE_TEMPLATE}"
if placeholder not in layer_content:
raise TemplateResolutionError(
f"Wrap layer {path} is missing {placeholder}"
)
content = layer_content.replace(placeholder, content)
else:
raise TemplateResolutionError(
f"Unknown template composition strategy '{strategy}' in {path}"
)
except (OSError, UnicodeError) as exc:
raise TemplateResolutionError(
f"Failed to read template layer for '{template_name}': {exc}"
) from exc
return content
override = (
repo_root
/ ".specify"
/ "templates"
/ "overrides"
/ f"{template_name}.md"
)
if override.is_file():
layers.append((override, "replace"))
return compose_from_base()View on GitHub (pinned to bf88c9f9a8)
Solutions
- Open the layer file named in the error message, find its manifest, and set strategy to one of: replace, prepend, append, or wrap (lowercase, exact).
- Check for trailing whitespace or capitalization in the strategy value (e.g. 'Prepend', 'prepend ').
- Upgrade docs awareness: there is no 'merge' or 'concat' strategy — restructure the preset using prepend/append/wrap.
Example fix
# before (manifest.yaml) strategy: pre-pend # after strategy: prepend
Defensive patterns
Strategy: validation
Validate before calling
ALLOWED = {"replace", "prepend", "append", "wrap"}
def strategy_is_valid(value) -> bool:
return isinstance(value, str) and value in ALLOWED
# validate all manifests before resolution
assert all(strategy_is_valid(m.get("strategy")) for m in loaded_manifests) Type guard
from typing import Any
ALLOWED = {"replace", "prepend", "append", "wrap"}
def is_strategy(value: Any) -> bool:
"""Narrow a manifest 'strategy' field to a known composition strategy."""
return isinstance(value, str) and value in ALLOWED Try / catch
try:
content = resolve_template_content(name, repo_root)
except TemplateResolutionError as exc:
if "Unknown template composition strategy" in str(exc):
raise SystemExit(f"Fix the manifest named in: {exc}") from exc
raise Prevention
- Validate strategy against the four allowed values when writing manifests.
- Keep a JSON-schema for preset manifests and check it in CI.
- Remember strategies are case-sensitive lowercase strings.
When it happens
Trigger: A manifest declares strategy: pre-pend, strategy: Wrap (case-sensitive), strategy: merge, or an empty/misspelled value; the layer loads fine but fails only at composition time, so the error message includes the specific path.
Common situations: Typos in hand-written preset manifests; strategy names copied from docs of a different tool version that supported extra strategies; trailing whitespace or a YAML quote issue making the value 'prepend ' with a trailing space.
Related errors
- Failed to parse preset manifest {manifest_path}: {exc}
- Wrap layer {path} is missing {placeholder}
- Template '{template_name}' has composing layers but no repla
- No runnable script variant for this platform: requested {req
- Invalid strategy value: must be a string, got {type(strategy
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/193022e361037f3b.
Report an issue: GitHub.