{"record":{"id":"3cfde31f76842db5","repo":"github/spec-kit","slug":"extension-ext-spec-is-in-the-catalog-name","errorCode":null,"errorMessage":"Extension '{ext_spec}' is in the '{catalog_name}' catalog but installation is not allowed from that catalog","messagePattern":"Extension '(.+?)' is in the '(.+?)' catalog but installation is not allowed from that catalog","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/commands/init.py","lineNumber":163,"sourceCode":"    if resolved_id != ext_spec:\n        bundled_path = _locate_bundled_extension(resolved_id)\n        if bundled_path is not None:\n            if manager.registry.is_installed(resolved_id):\n                return \"already installed\"\n            manifest = manager.install_from_directory(bundled_path, speckit_version)\n            return f\"{manifest.name} v{manifest.version} installed\"\n\n    if ext_info.get(\"bundled\") and not ext_info.get(\"download_url\"):\n        from ..extensions import REINSTALL_COMMAND\n\n        raise ValueError(\n            f\"Extension '{resolved_id}' is bundled with spec-kit but not found in the installed package. \"\n            f\"Try reinstalling spec-kit: {REINSTALL_COMMAND}\"\n        )\n\n    if not ext_info.get(\"_install_allowed\", True):\n        catalog_name = ext_info.get(\"_catalog_name\", \"community\")\n        raise ValueError(\n            f\"Extension '{ext_spec}' is in the '{catalog_name}' catalog but installation is not allowed from that catalog\"\n        )\n\n    zip_path = catalog.download_extension(resolved_id)\n    try:\n        manifest = manager.install_from_zip(zip_path, speckit_version)\n    finally:\n        zip_path.unlink(missing_ok=True)\n    return f\"{manifest.name} v{manifest.version} installed\"\n\n\ndef _shell_quote_arg(value: str) -> str:\n    \"\"\"Quote *value* as one argument for the shells of the host OS.\n\n    The Next Steps ``cd`` line is copy-pasted into whichever shell ran\n    ``specify init``, so it is quoted for the host the same way\n    ``_version._render_argv`` renders its copy-pasteable installer command:\n    ``list2cmdline`` on Windows, ``shlex.quote`` elsewhere. The Windows branch","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/commands/init.py#L145-L181","documentation":"Catalogs carry an installation policy; the built-in community catalog is discovery-only (`install_allowed: false`). When the resolved extension comes from such a catalog, init refuses to download it, even though search/info can display it. This is a deliberate trust-boundary control.","triggerScenarios":"`specify init --extension <id>` resolves only from the community catalog or another catalog configured with `install_allowed: false`.","commonSituations":"Trying to install a community-contributed extension that has not been promoted to the default/official catalog, or adding an org catalog with discovery-only policy.","solutions":["Check which catalog produced the entry with extension search/info; the error names it.","Use an equivalent extension from an approved/default catalog if one exists.","Have an administrator curate the extension into a trusted catalog and configure `.specify/extension-catalogs.yml` with `install_allowed: true`.","Only if you trust the source, install it explicitly from a local path or reviewed HTTPS URL instead of bypassing catalog policy."],"exampleFix":"# .specify/extension-catalogs.yml (after explicit approval)\ncatalogs:\n  - name: approved-org-catalog\n    url: https://catalog.example.com/catalog.json\n    priority: 1\n    install_allowed: true\n    description: Extensions approved by this organization","handlingStrategy":"validation","validationCode":"from pathlib import Path\nfrom specify_cli.extensions import ExtensionCatalog\n\ndef extension_install_is_allowed(ext_spec: str, project_root: Path) -> bool:\n    info = ExtensionCatalog(project_root).get_extension_info(ext_spec)\n    return bool(info) and bool(info.get(\"_install_allowed\", True))","typeGuard":null,"tryCatchPattern":"try:\n    _install_extension_during_init(project_path, ext_spec, version)\nexcept ValueError as exc:\n    if \"installation is not allowed\" in str(exc):\n        request_curated_catalog_entry(ext_spec)\n    else:\n        raise","preventionTips":["Maintain an org-approved catalog with install_allowed: true for vetted extensions.","Do not automatically flip community catalogs to installable; review extensions first.","Record the catalog source and policy beside every extension used by a project."],"tags":["security","policy","extensions","catalog","init"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}