github/spec-kit · error · PresetValidationError

Invalid extension registry {registry.registry_path}: refusin

Error message

Invalid extension registry {registry.registry_path}: refusing to enumerate extensions

What it means

The extensions registry file is corrupt and enumeration fails closed: ExtensionRegistry._load() would silently normalize an unreadable registry to empty, which would let every on-disk extension directory be treated as unregistered-and-enabled — a fail-open path that could inject constitution content. The presets layer therefore raises PresetValidationError and refuses to enumerate.

Source

Thrown at src/specify_cli/presets/__init__.py:5132

        Registered extensions use their stored priority; unregistered directories
        get implicit priority=10. Results are sorted by (priority, ext_id) for
        deterministic ordering.

        Returns:
            List of (priority, ext_id, metadata_or_none) tuples sorted by priority.
        """
        if not self.extensions_dir.exists():
            return []

        registry = ExtensionRegistry(self.extensions_dir)
        # Fail closed on a corrupt registry. ExtensionRegistry._load() recovers
        # by normalizing an unreadable registry to an empty mapping, which would
        # otherwise cause the directory scan below to admit every on-disk
        # directory as an unregistered, enabled extension — a fail-open path
        # that could supply constitution content from an invalid registry state.
        if registry.is_corrupt():
            raise PresetValidationError(
                f"Invalid extension registry {registry.registry_path}: "
                "refusing to enumerate extensions"
            )
        # Use keys() to track ALL extensions (including corrupted entries) without deep copy
        # This prevents corrupted entries from being picked up as "unregistered" dirs
        registered_extension_ids = registry.keys()

        # Get all registered extensions including disabled; we filter disabled manually below
        all_registered = registry.list_by_priority(include_disabled=True)

        all_extensions: list[tuple[int, str, dict | None]] = []

        # Only include enabled extensions in the result
        for ext_id, metadata in all_registered:
            if not self._is_safe_registry_id(ext_id):
                continue
            # Skip disabled extensions
            if not metadata.get("enabled", True):

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Inspect the registry file (registry.registry_path) and fix its JSON syntax or structure
  2. If unrecoverable, restore it from version control or delete it so it is rebuilt from the on-disk extension state
  3. Avoid editing the registry while spec-kit commands run; keep writes atomic
  4. Re-run the enumeration after repair to confirm is_corrupt() is False

Example fix

# before (registry.json truncated)
{"extensions": {"agent-context": {"prior

# after
{"extensions": {"agent-context": {"priority": 100, "enabled": true}}}
Defensive patterns

Strategy: try-catch

Validate before calling

from specify_cli.extensions import ExtensionRegistry
reg = ExtensionRegistry(extensions_dir)
if reg.is_corrupt():
    repair_or_restore_registry(reg.registry_path)  # fix JSON or restore from VCS

Try / catch

try:
    exts = enumerate_extensions(manager)
except PresetValidationError as e:
    if "Invalid extension registry" in str(e):
        restore_registry_from_backup(extensions_dir)  # then retry once
    else:
        raise

Prevention

When it happens

Trigger: Calling the extension-enumeration flow while .specify/extensions registry JSON is malformed (truncated write, invalid JSON, wrong structure) so registry.is_corrupt() returns True.

Common situations: Concurrent writes or a killed process truncating the registry file; hand-editing the registry JSON; partial sync/merge of the .specify directory; disk corruption.

Related errors


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