{"record":{"id":"193022e361037f3b","repo":"github/spec-kit","slug":"unknown-template-composition-strategy-strategy","errorCode":null,"errorMessage":"Unknown template composition strategy '{strategy}' in {path}","messagePattern":"Unknown template composition strategy '(.+?)' in (.+?)","errorType":"exception","errorClass":"TemplateResolutionError","httpStatus":null,"severity":"error","filePath":"scripts/python/common.py","lineNumber":469,"sourceCode":"\n    def compose_from_base() -> str:\n        try:\n            content = layers[-1][0].read_bytes().decode(\"utf-8\")\n            for path, strategy in reversed(layers[:-1]):\n                layer_content = path.read_bytes().decode(\"utf-8\")\n                if strategy == \"prepend\":\n                    content = f\"{layer_content}\\n\\n{content}\"\n                elif strategy == \"append\":\n                    content = f\"{content}\\n\\n{layer_content}\"\n                elif strategy == \"wrap\":\n                    placeholder = \"{CORE_TEMPLATE}\"\n                    if placeholder not in layer_content:\n                        raise TemplateResolutionError(\n                            f\"Wrap layer {path} is missing {placeholder}\"\n                        )\n                    content = layer_content.replace(placeholder, content)\n                else:\n                    raise TemplateResolutionError(\n                        f\"Unknown template composition strategy '{strategy}' in {path}\"\n                    )\n        except (OSError, UnicodeError) as exc:\n            raise TemplateResolutionError(\n                f\"Failed to read template layer for '{template_name}': {exc}\"\n            ) from exc\n        return content\n\n    override = (\n        repo_root\n        / \".specify\"\n        / \"templates\"\n        / \"overrides\"\n        / f\"{template_name}.md\"\n    )\n    if override.is_file():\n        layers.append((override, \"replace\"))\n        return compose_from_base()","sourceCodeStart":451,"sourceCodeEnd":487,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/scripts/python/common.py#L451-L487","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before (manifest.yaml)\nstrategy: pre-pend\n# after\nstrategy: prepend","handlingStrategy":"validation","validationCode":"ALLOWED = {\"replace\", \"prepend\", \"append\", \"wrap\"}\n\ndef strategy_is_valid(value) -> bool:\n    return isinstance(value, str) and value in ALLOWED\n\n# validate all manifests before resolution\nassert all(strategy_is_valid(m.get(\"strategy\")) for m in loaded_manifests)","typeGuard":"from typing import Any\n\nALLOWED = {\"replace\", \"prepend\", \"append\", \"wrap\"}\n\ndef is_strategy(value: Any) -> bool:\n    \"\"\"Narrow a manifest 'strategy' field to a known composition strategy.\"\"\"\n    return isinstance(value, str) and value in ALLOWED","tryCatchPattern":"try:\n    content = resolve_template_content(name, repo_root)\nexcept TemplateResolutionError as exc:\n    if \"Unknown template composition strategy\" in str(exc):\n        raise SystemExit(f\"Fix the manifest named in: {exc}\") from exc\n    raise","preventionTips":["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."],"tags":["templates","preset","configuration","strategy"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}