{"record":{"id":"e15f17ae55dfa14e","repo":"github/spec-kit","slug":"failed-to-read-template-layer-for-template-name","errorCode":null,"errorMessage":"Failed to read template layer for '{template_name}': {exc}","messagePattern":"Failed to read template layer for '(.+?)': (.+?)","errorType":"exception","errorClass":"TemplateResolutionError","httpStatus":null,"severity":"error","filePath":"scripts/python/common.py","lineNumber":473,"sourceCode":"            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()\n\n    presets_dir = repo_root / \".specify\" / \"presets\"\n    for preset_id in _sorted_preset_ids(presets_dir):\n        layer = _preset_template_layer(presets_dir / preset_id, template_name)","sourceCodeStart":455,"sourceCodeEnd":491,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/scripts/python/common.py#L455-L491","documentation":"While walking the template layer stack, compose_from_base() reads each layer file with read_bytes().decode(\"utf-8\"). Any OSError (missing file, permission denied) or UnicodeError (non-UTF-8 bytes) is converted into TemplateResolutionError with the template name and the underlying exception, so the raw filesystem/encoding failure is never silently ignored.","triggerScenarios":"A layer path recorded from the preset/override resolution disappears before it is read (race with deletion, broken symlink); a layer file is chmod 000 or owned by another user; a layer saved as latin-1/UTF-16 containing bytes invalid in UTF-8.","commonSituations":"Concurrent git operations (checkout/rebase) removing a preset file mid-run; templates copied from Windows saved as UTF-16; permission changes after running under a different user (root-created files then run as normal user).","solutions":["Check the underlying exception in the message: for FileNotFoundError, restore the missing layer file (git checkout -- <path>) or remove its manifest entry.","For a decode error, re-save the layer file as UTF-8 (iconv -f UTF-16 -t UTF-8 or editor 'Save with encoding UTF-8').","For permission errors, fix ownership/permissions (chmod 644 <layer>, chown) so the running user can read it."],"exampleFix":"# before — layer saved as UTF-16 (UnicodeDecodeError at compose time)\n# after — convert and re-save\n# iconv -f UTF-16 -t UTF-8 layer.md > layer.utf8.md && mv layer.utf8.md layer.md","handlingStrategy":"try-catch","validationCode":"from pathlib import Path\n\ndef layers_are_readable(layers: list[tuple[Path, str]]) -> list[Path]:\n    bad = []\n    for path, _ in layers:\n        try:\n            path.read_bytes().decode(\"utf-8\")\n        except (OSError, UnicodeError):\n            bad.append(path)\n    return bad\n\n# before composing\nif problems := layers_are_readable(layers):\n    print(f\"unreadable layers: {problems}\")","typeGuard":null,"tryCatchPattern":"try:\n    content = resolve_template_content(name, repo_root)\nexcept TemplateResolutionError as exc:\n    if \"Failed to read template layer\" in str(exc):\n        logger.warning(\"template %s unavailable (%s); falling back to default\", name, exc)\n        content = None\n    else:\n        raise","preventionTips":["Keep template files owned/readable by the user running the CLI.","Save all template layers as UTF-8.","Avoid mutating .specify/templates while a specify command is running."],"tags":["templates","filesystem","encoding","composition"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}