github/spec-kit · error · TemplateResolutionError

Failed to read template layer for '{template_name}': {exc}

Error message

Failed to read template layer for '{template_name}': {exc}

What it means

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.

Source

Thrown at scripts/python/common.py:473

            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()

    presets_dir = repo_root / ".specify" / "presets"
    for preset_id in _sorted_preset_ids(presets_dir):
        layer = _preset_template_layer(presets_dir / preset_id, template_name)

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Check the underlying exception in the message: for FileNotFoundError, restore the missing layer file (git checkout -- <path>) or remove its manifest entry.
  2. 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').
  3. For permission errors, fix ownership/permissions (chmod 644 <layer>, chown) so the running user can read it.

Example fix

# before — layer saved as UTF-16 (UnicodeDecodeError at compose time)
# after — convert and re-save
# iconv -f UTF-16 -t UTF-8 layer.md > layer.utf8.md && mv layer.utf8.md layer.md
Defensive patterns

Strategy: try-catch

Validate before calling

from pathlib import Path

def layers_are_readable(layers: list[tuple[Path, str]]) -> list[Path]:
    bad = []
    for path, _ in layers:
        try:
            path.read_bytes().decode("utf-8")
        except (OSError, UnicodeError):
            bad.append(path)
    return bad

# before composing
if problems := layers_are_readable(layers):
    print(f"unreadable layers: {problems}")

Try / catch

try:
    content = resolve_template_content(name, repo_root)
except TemplateResolutionError as exc:
    if "Failed to read template layer" in str(exc):
        logger.warning("template %s unavailable (%s); falling back to default", name, exc)
        content = None
    else:
        raise

Prevention

When it happens

Trigger: 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.

Common situations: 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).

Related errors


AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14). Data as JSON: /api/errors/e15f17ae55dfa14e. Report an issue: GitHub.