{"record":{"id":"f1da1b038a0251d7","repo":"github/spec-kit","slug":"bundle-bundle-id-resolves-only-from-a-discover","errorCode":null,"errorMessage":"Bundle '{bundle_id}' resolves only from a discovery-only source ('{resolved.source.id}'); it cannot be installed from there.","messagePattern":"Bundle '(.+?)' resolves only from a discovery-only source \\('(.+?)'\\); it cannot be installed from there\\.","errorType":"exception","errorClass":"BundlerError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/commands/bundle/__init__.py","lineNumber":383,"sourceCode":"        from ...bundler.services.adapters import DefaultPrimitiveInstaller\n        from ...bundler.services.installer import install_bundle\n        from ...bundler.services.resolver import resolve_install_plan\n\n        project_root = find_project_root()\n\n        local_manifest = _local_manifest_source(bundle_id)\n        if local_manifest is not None:\n            manifest = local_manifest\n            _validate_manifest_structure(\n                manifest,\n                source=f\"Local bundle source {bundle_id!r}\",\n            )\n        else:\n            stack = _build_stack(project_root or Path.cwd(), offline=offline)\n            resolved = stack.resolve(bundle_id)\n\n            if not resolved.install_allowed:\n                raise BundlerError(\n                    f\"Bundle '{bundle_id}' resolves only from a discovery-only source \"\n                    f\"('{resolved.source.id}'); it cannot be installed from there.\"\n                )\n            manifest = _download_manifest(resolved, offline=offline)\n\n        if project_root is None:\n            init_integration = _resolve_init_integration(integration, manifest)\n            # Resolve all hard compatibility gates before ``specify init``.\n            # Otherwise an incompatible but structurally valid bundle would\n            # initialize a project and only then fail its version/integration\n            # checks, leaving state behind after a failed install.\n            resolve_install_plan(\n                manifest,\n                speckit_version=_speckit_version(),\n                active_integration=init_integration,\n                integration_explicit=True,\n            )\n            console.print(","sourceCodeStart":365,"sourceCodeEnd":401,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/commands/bundle/__init__.py#L365-L401","documentation":"During bundle install, the bundle id resolved through the catalog stack to a source whose install_allowed is False (a discovery-only source, e.g. a search/index registry). Discovery-only sources can tell you a bundle exists but are not permitted installation origins, so the CLI refuses before downloading a manifest. Mirrors the FR-025 rule enforced on update.","triggerScenarios":"specify bundle install <id> (or the API path: _build_stack(...).resolve(id) then checking resolved.install_allowed) where the id is only present in a source registered with install_allowed=False and no local ./bundle.yml or path argument was given.","commonSituations":"A global search catalog that indexes bundles hosted elsewhere; corporate registries split into 'browse' and 'install' mirrors; the install-enabled source is offline/unreachable so resolution fell through to the discovery-only one.","solutions":["Find the bundle's real install source (its published repo/URL) and install from a path: specify bundle install ./bundle.yml, a bundle directory, or a .zip artifact.","Configure the catalog stack so the bundle is resolvable from an install-allowed source.","If you operate the discovery source, add a download_url and mark it install-allowed."],"exampleFix":"# before\nspecify bundle install some-indexed-bundle\n\n# after — install from the actual artifact\nspecify bundle install ./some-indexed-bundle.zip","handlingStrategy":"validation","validationCode":"stack = _build_stack(project_root, offline=offline)\nresolved = stack.resolve(bundle_id)\nif not resolved.install_allowed:\n    raise SystemExit(\n        f\"{bundle_id} is discovery-only on '{resolved.source.id}'; \"\n        \"install from an artifact path instead\"\n    )","typeGuard":"def is_installable(resolved) -> bool:\n    \"\"\"True when the resolved source permits installation.\"\"\"\n    return resolved.install_allowed","tryCatchPattern":"try:\n    bundle_install(...)\nexcept BundlerError as exc:\n    if \"discovery-only source\" in str(exc):\n        # fall back to a local artifact path install\n        ...","preventionTips":["Check resolved.install_allowed after stack.resolve() before attempting install.","Keep a local copy (.zip / directory) of bundles you rely on.","Know which of your configured catalog sources are install-allowed."],"tags":["bundler","catalog","source","policy"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}