github/spec-kit · error · TemplateResolutionError
Invalid extension registry {registry}: root must be a mappin
Error message
Invalid extension registry {registry}: root must be a mapping What it means
_StagedWorkflowFile._write() loops over chunks with os.write(); POSIX write() returning <= 0 without raising is treated as failure ('Failed to write staged workflow file', OSError). For regular files this essentially never happens — a 0 return on a non-zero request indicates a broken fd or an exotic/filesystem-level fault, so the staged install aborts rather than produce a truncated file.
Source
Thrown at scripts/python/common.py:258
def _sorted_extension_ids(extensions_dir: Path) -> list[str]:
registry = extensions_dir / ".registry"
registered_ids: set[str] = set()
extensions: dict[object, object] = {}
if os.path.lexists(registry):
if not registry.is_file():
raise TemplateResolutionError(
f"Invalid extension registry {registry}: not a regular file"
)
try:
data = json.loads(registry.read_text(encoding="utf-8"))
except (OSError, UnicodeError, json.JSONDecodeError) as exc:
raise TemplateResolutionError(
f"Failed to parse extension registry {registry}: {exc}"
) from exc
if not isinstance(data, dict):
raise TemplateResolutionError(
f"Invalid extension registry {registry}: root must be a mapping"
)
raw_extensions = data.get("extensions", {})
if not isinstance(raw_extensions, dict):
raise TemplateResolutionError(
f"Invalid extension registry {registry}: "
"'extensions' must be a mapping"
)
extensions = raw_extensions
registered_ids = {
ext_id for ext_id in extensions if isinstance(ext_id, str)
}
ranked: list[tuple[int, str]] = []
for ext_id, metadata in extensions.items():
if (
_is_safe_component(ext_id)
and isinstance(metadata, dict)View on GitHub (pinned to bf88c9f9a8)
Solutions
- Retry the workflow install once — transient filesystem faults usually clear
- Move the project (or at least .specify) to a local, POSIX-compliant filesystem (ext4/APFS) and retry
- Check df -h and dmesg for ENOSPC or I/O errors at the time of failure
- If it reproduces consistently on one machine, capture the fd state (ls -l /proc/<pid>/fd) and report as a filesystem-level bug
Example fix
# before: staging on a flaky network mount $ specify workflow add ... # OSError: Failed to write staged workflow file # after $ mv project ~/local-disk/ && cd ~/local-disk/project $ specify workflow add ... # ok
Defensive patterns
Strategy: retry
Validate before calling
import os
def fd_is_writable(fd: int) -> bool:
try:
return os.write(fd, b'') == 0 # zero-length write is a no-op probe
except OSError:
return False Try / catch
try:
staged.write_bytes(data)
except OSError as e:
if 'Failed to write staged workflow file' in str(e):
staged = reopen_staged_file() # new fd, fresh attempt once
staged.write_bytes(data)
else:
raise Prevention
- Run installs on a local POSIX filesystem, not FUSE/network mounts
- Monitor df -h/dmesg for ENOSPC and I/O errors on build machines
- Retry once before investigating — degenerate write() results are usually transient
When it happens
Trigger: os.write() to the O_NOFOLLOW-opened staged fd returns 0 or negative — possible with a closed/reused fd, a FUSE/network filesystem returning degenerate results under load, or an interrupted/instrumented runtime.
Common situations: Writing the staged workflow onto sshfs/NFS/FUSE mounts with flaky semantics; a bug elsewhere closing the fd prematurely; sandboxed runtimes (seccomp/LSAN-instrumented) altering syscall behavior; disk-full edge cases on exotic filesystems that return 0 instead of ENOSPC.
Related errors
- Invalid extension registry {registry}: 'extensions' must be
- Error: branch_template must include the {number} token so ge
- ERROR: Feature directory not found. Set SPECIFY_FEATURE_DIRE
- PyYAML is required to resolve preset template composition
- specify event ${{command}} (${{event}}) failed: ${{(e as Err
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/812da7002da7ace7.
Report an issue: GitHub.