{"record":{"id":"954d5523d73d4dec","repo":"github/spec-kit","slug":"catalog-payload-is-missing-a-bundles-object","errorCode":null,"errorMessage":"Catalog payload is missing a 'bundles' object.","messagePattern":"Catalog payload is missing a 'bundles' object\\.","errorType":"exception","errorClass":"BundlerError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/bundler/models/catalog.py","lineNumber":220,"sourceCode":"        return CatalogEntry(\n            id=self.id, name=self.name, version=self.version, role=self.role,\n            description=self.description, author=self.author, license=self.license,\n            download_url=self.download_url,\n            requires_speckit_version=self.requires_speckit_version,\n            sha256=self.sha256,\n            provides=self.provides, repository=self.repository, tags=self.tags,\n            verified=self.verified, source_id=source.id,\n            source_policy=source.install_policy,\n        )\n\n\ndef load_catalog_payload(data: Any) -> dict[str, CatalogEntry]:\n    \"\"\"Parse a catalog JSON payload into ``{bundle_id: CatalogEntry}``.\"\"\"\n    if not isinstance(data, dict):\n        raise BundlerError(\"Catalog payload must be a JSON object.\")\n    bundles_raw = data.get(\"bundles\")\n    if not isinstance(bundles_raw, dict):\n        raise BundlerError(\"Catalog payload is missing a 'bundles' object.\")\n    entries: dict[str, CatalogEntry] = {}\n    for bundle_id, entry_raw in bundles_raw.items():\n        key = str(bundle_id)\n        entry = CatalogEntry.from_dict(entry_raw)\n        # The enclosing key is the authoritative bundle id used by\n        # search/resolve/install. Reject entries whose own ``id`` is missing or\n        # disagrees with the key, so a malformed or malicious catalog can't list\n        # an id that resolves to a different (or no) bundle.\n        if not entry.id:\n            raise BundlerError(\n                f\"Catalog entry for '{key}' is missing its 'id' field.\"\n            )\n        if entry.id != key:\n            raise BundlerError(\n                f\"Catalog entry id mismatch: key '{key}' != entry id \"\n                f\"'{entry.id}'.\"\n            )\n        entries[key] = entry","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/bundler/models/catalog.py#L202-L238","documentation":"Raised by load_catalog_payload() when the top-level object is present but its 'bundles' value is missing or not a dict. 'bundles' is the mandatory map of bundle-id to entry object; null, a list, or a string in that slot raises this error.","triggerScenarios":"A catalog object like {\"version\": 2} with no 'bundles' key; \"bundles\": [...] (array); \"bundles\": null; schema renamed in a newer catalog format.","commonSituations":"Version mismatch between catalog format generations; partial or truncated catalogs; hand-authored catalogs omitting the wrapper; upstream API change at a catalog host.","solutions":["Add a 'bundles' object mapping bundle ids to entry objects.","If the catalog uses a newer format, update specify/spec-kit to a version that supports it.","Verify with curl/jq that .bundles is an object before wiring the source in.","Remove or replace catalog sources that persistently serve bundle-less payloads."],"exampleFix":"# before (catalog JSON)\n{\"metadata\": {\"generated\": \"2026-01-01\"}}\n\n# after (catalog JSON)\n{\"bundles\": {\"my-bundle\": {\"id\": \"my-bundle\", ...}}}","handlingStrategy":"validation","validationCode":"bundles = data.get(\"bundles\") if isinstance(data, dict) else None\nif not isinstance(bundles, dict):\n    raise ValueError(\"catalog payload missing a 'bundles' object\")\nload_catalog_payload(data)","typeGuard":"def has_bundles_object(data: object) -> bool:\n    return isinstance(data, dict) and isinstance(data.get(\"bundles\"), dict)","tryCatchPattern":"try:\n    load_catalog_payload(data)\nexcept BundlerError as e:\n    if \"'bundles' object\" in str(e):\n        skip_source_with_schema_warning(source)","preventionTips":["Check .bundles exists and is an object (jq '.bundles | type') before adding a source.","Pin to catalog format versions your specify release supports.","Alert catalog maintainers when their format changes break consumers."],"tags":["bundler","catalog","validation","payload","missing-field"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}