{"record":{"id":"c2ed2db7deb36a9c","repo":"github/spec-kit","slug":"extension-component-id-is-from-a-discovery-onl","errorCode":null,"errorMessage":"Extension '{component.id}' is from a discovery-only catalog; installation is not allowed.","messagePattern":"Extension '(.+?)' is from a discovery-only catalog; installation is not allowed\\.","errorType":"exception","errorClass":"BundlerError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/bundler/services/primitives.py","lineNumber":287,"sourceCode":"            return\n\n        if not self._allow_network:\n            raise BundlerError(\n                f\"Extension '{component.id}' is not bundled and network access is \"\n                f\"disabled; re-run without --offline or install it first with \"\n                f\"'specify extension add {component.id}'.\"\n            )\n\n        from ...extensions import ExtensionCatalog\n\n        catalog = ExtensionCatalog(self._root)\n        info = catalog.get_extension_info(component.id)\n        if not info:\n            raise BundlerError(\n                f\"Extension '{component.id}' not found in any catalog.\"\n            )\n        if not info.get(\"_install_allowed\", True):\n            raise BundlerError(\n                f\"Extension '{component.id}' is from a discovery-only catalog; \"\n                \"installation is not allowed.\"\n            )\n        _assert_pinned_version(\n            \"Extension\", component.id, component.version, info.get(\"version\")\n        )\n        zip_path = catalog.download_extension(component.id)\n        try:\n            self._manager.install_from_zip(\n                zip_path, speckit_version, priority=priority, force=force\n            )\n        finally:\n            with contextlib.suppress(Exception):\n                if zip_path.exists():\n                    zip_path.unlink()\n\n    def remove(self, component: ComponentRef) -> None:\n        try:","sourceCodeStart":269,"sourceCodeEnd":305,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/bundler/services/primitives.py#L269-L305","documentation":"The extension was found in a catalog, but that catalog is discovery-only: its entry has _install_allowed=False, so the bundler refuses to download and install it as part of a bundle. The check fires before the pinned-version comparison and before download_extension().","triggerScenarios":"A bundle referencing an extension listed in a read-only index catalog — the id resolves (info is truthy) but the catalog forbids installs from it.","commonSituations":"Curated indexes of third-party extensions; enterprise catalogs that list approved extensions but host them elsewhere; bundle authors picking ids from a listing without an install source.","solutions":["Locate the installable source for the extension and add it to your catalog configuration, or install it directly before the bundle run.","Vendor the extension into the bundle artifact so no catalog install is attempted.","Drop the extension from bundle.yml if it is not needed."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"from specify_cli.extensions import ExtensionCatalog\n\ninfo = ExtensionCatalog(project_root).get_extension_info(component_id)\nif info and not info.get(\"_install_allowed\", True):\n    print(f\"{component_id} is discovery-only; choose an installable source\")","typeGuard":null,"tryCatchPattern":"from specify_cli.bundler.core import BundlerError\n\ntry:\n    install_bundle(project_root, plan, installer)\nexcept BundlerError as exc:\n    if \"discovery-only catalog\" in str(exc):\n        switch_component_to_installable_source(plan)","preventionTips":["Check _install_allowed on catalog entries before adding extensions to bundles.","Do not source extension ids from index/discovery listings.","Vendor discovery-only extensions into the bundle artifact."],"tags":["bundler","extension","catalog","policy"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}