{"record":{"id":"e45e8a1d50f8869f","repo":"github/spec-kit","slug":"invalid-extension-registry-registry-registry-path","errorCode":null,"errorMessage":"Invalid extension registry {registry.registry_path}: refusing to enumerate extensions","messagePattern":"Invalid extension registry (.+?): refusing to enumerate extensions","errorType":"validation","errorClass":"PresetValidationError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/presets/__init__.py","lineNumber":5132,"sourceCode":"\n        Registered extensions use their stored priority; unregistered directories\n        get implicit priority=10. Results are sorted by (priority, ext_id) for\n        deterministic ordering.\n\n        Returns:\n            List of (priority, ext_id, metadata_or_none) tuples sorted by priority.\n        \"\"\"\n        if not self.extensions_dir.exists():\n            return []\n\n        registry = ExtensionRegistry(self.extensions_dir)\n        # Fail closed on a corrupt registry. ExtensionRegistry._load() recovers\n        # by normalizing an unreadable registry to an empty mapping, which would\n        # otherwise cause the directory scan below to admit every on-disk\n        # directory as an unregistered, enabled extension — a fail-open path\n        # that could supply constitution content from an invalid registry state.\n        if registry.is_corrupt():\n            raise PresetValidationError(\n                f\"Invalid extension registry {registry.registry_path}: \"\n                \"refusing to enumerate extensions\"\n            )\n        # Use keys() to track ALL extensions (including corrupted entries) without deep copy\n        # This prevents corrupted entries from being picked up as \"unregistered\" dirs\n        registered_extension_ids = registry.keys()\n\n        # Get all registered extensions including disabled; we filter disabled manually below\n        all_registered = registry.list_by_priority(include_disabled=True)\n\n        all_extensions: list[tuple[int, str, dict | None]] = []\n\n        # Only include enabled extensions in the result\n        for ext_id, metadata in all_registered:\n            if not self._is_safe_registry_id(ext_id):\n                continue\n            # Skip disabled extensions\n            if not metadata.get(\"enabled\", True):","sourceCodeStart":5114,"sourceCodeEnd":5150,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/presets/__init__.py#L5114-L5150","documentation":"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.","triggerScenarios":"Calling the extension-enumeration flow while .specify/extensions registry JSON is malformed (truncated write, invalid JSON, wrong structure) so registry.is_corrupt() returns True.","commonSituations":"Concurrent writes or a killed process truncating the registry file; hand-editing the registry JSON; partial sync/merge of the .specify directory; disk corruption.","solutions":["Inspect the registry file (registry.registry_path) and fix its JSON syntax or structure","If unrecoverable, restore it from version control or delete it so it is rebuilt from the on-disk extension state","Avoid editing the registry while spec-kit commands run; keep writes atomic","Re-run the enumeration after repair to confirm is_corrupt() is False"],"exampleFix":"# before (registry.json truncated)\n{\"extensions\": {\"agent-context\": {\"prior\n\n# after\n{\"extensions\": {\"agent-context\": {\"priority\": 100, \"enabled\": true}}}","handlingStrategy":"try-catch","validationCode":"from specify_cli.extensions import ExtensionRegistry\nreg = ExtensionRegistry(extensions_dir)\nif reg.is_corrupt():\n    repair_or_restore_registry(reg.registry_path)  # fix JSON or restore from VCS","typeGuard":null,"tryCatchPattern":"try:\n    exts = enumerate_extensions(manager)\nexcept PresetValidationError as e:\n    if \"Invalid extension registry\" in str(e):\n        restore_registry_from_backup(extensions_dir)  # then retry once\n    else:\n        raise","preventionTips":["Keep .specify/extensions registry in version control for easy restore","Never hand-edit the registry JSON while spec-kit commands are running","Treat registry corruption as fail-closed: repair before enumerating, do not bypass the check"],"tags":["extensions","registry","corruption","security","fail-closed"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}