{"record":{"id":"e93dba195c730378","repo":"github/spec-kit","slug":"invalid-catalog-format-from-url","errorCode":null,"errorMessage":"Invalid catalog format from {url}","messagePattern":"Invalid catalog format from (.+?)","errorType":"exception","errorClass":"ExtensionError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/extensions/__init__.py","lineNumber":3694,"sourceCode":"        object and the nested mapping (see ``integrations/catalog.py``);\n        the extension catalog must stay consistent so a malformed payload\n        surfaces as the user-facing ``Invalid catalog format`` error\n        instead of a raw Python traceback.\n\n        Args:\n            catalog_data: Parsed JSON payload from the catalog source.\n            url: Source URL — used in the error message so the user can\n                tell which catalog in a multi-catalog stack is malformed.\n\n        Raises:\n            ExtensionError: If the payload's shape is invalid.\n        \"\"\"\n        if not isinstance(catalog_data, dict):\n            raise ExtensionError(\n                f\"Invalid catalog format from {url}: expected a JSON object\"\n            )\n        if \"schema_version\" not in catalog_data or \"extensions\" not in catalog_data:\n            raise ExtensionError(f\"Invalid catalog format from {url}\")\n        if not isinstance(catalog_data.get(\"extensions\"), dict):\n            raise ExtensionError(\n                f\"Invalid catalog format from {url}: 'extensions' must be a JSON object\"\n            )\n\n    def get_active_catalogs(self) -> List[CatalogEntry]:\n        \"\"\"Get the ordered list of active catalogs.\n\n        Resolution order:\n        1. SPECKIT_CATALOG_URL env var — single catalog replacing all defaults\n        2. Project-level .specify/extension-catalogs.yml\n        3. User-level ~/.specify/extension-catalogs.yml\n        4. Built-in default stack (default + community)\n\n        Returns:\n            List of CatalogEntry objects sorted by priority (ascending)\n\n        Raises:","sourceCodeStart":3676,"sourceCodeEnd":3712,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/extensions/__init__.py#L3676-L3712","documentation":"Second stage of catalog validation: the payload is a JSON object but is missing either the schema_version or the extensions key (or both). Both keys are mandatory for a spec-kit extension catalog, so an object lacking them is rejected as an invalid catalog format.","triggerScenarios":"A catalog URL returning `{\"data\": ...}` or an extensions-only object without schema_version; any object payload where 'schema_version' not in catalog_data or 'extensions' not in catalog_data evaluates true.","commonSituations":"Hand-authoring a catalog and omitting schema_version; server returning a generic API envelope ({'status':'ok',...}); typos like extension/version singular forms.","solutions":["Add both required keys to the catalog document: schema_version (e.g. 1) and extensions (object keyed by extension id)","Check for key typos — plural 'extensions' and exact 'schema_version' are required","Validate locally before publishing: `python -c \"import json;d=json.load(open('catalog.json'));assert {'schema_version','extensions'} <= d.keys()\"`","If you don't control the URL, remove it from the catalog config and rely on the built-in default+community stack"],"exampleFix":"# before\n{\n  \"extensions\": { \"my-ext\": {\"name\": \"My Ext\"} }\n}\n\n# after\n{\n  \"schema_version\": 1,\n  \"extensions\": { \"my-ext\": {\"name\": \"My Ext\"} }\n}","handlingStrategy":"validation","validationCode":"REQUIRED_CATALOG_KEYS = {'schema_version', 'extensions'}\n\ndef catalog_has_required_keys(data: dict) -> bool:\n    return REQUIRED_CATALOG_KEYS <= data.keys()","typeGuard":"def is_catalog_shaped(data: object) -> bool:\n    return (\n        isinstance(data, dict)\n        and 'schema_version' in data\n        and 'extensions' in data\n        and isinstance(data['extensions'], dict)\n    )","tryCatchPattern":null,"preventionTips":["Copy the documented catalog skeleton rather than authoring from scratch","Run a schema assertion in the catalog's publish pipeline"],"tags":["extensions","catalog","json","schema"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}