{"record":{"id":"157b8c9ade3e6293","repo":"github/spec-kit","slug":"a-catalog-source-is-missing-its-id","errorCode":null,"errorMessage":"A catalog source is missing its 'id'.","messagePattern":"A catalog source is missing its 'id'\\.","errorType":"exception","errorClass":"BundlerError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/bundler/models/catalog.py","lineNumber":75,"sourceCode":"class CatalogSource:\n    id: str\n    url: str\n    priority: int\n    install_policy: InstallPolicy\n    scope: Scope = Scope.PROJECT\n\n    @property\n    def install_allowed(self) -> bool:\n        return self.install_policy is InstallPolicy.INSTALL_ALLOWED\n\n    @classmethod\n    def from_dict(cls, data: Any, scope: Scope) -> \"CatalogSource\":\n        if not isinstance(data, dict):\n            raise BundlerError(\"Each catalog source must be a mapping.\")\n        source_id = str(data.get(\"id\", \"\")).strip()\n        url = str(data.get(\"url\", \"\")).strip()\n        if not source_id:\n            raise BundlerError(\"A catalog source is missing its 'id'.\")\n        if not url:\n            raise BundlerError(f\"Catalog source '{source_id}' is missing its 'url'.\")\n        priority = data.get(\"priority\")\n        if priority is None:\n            raise BundlerError(f\"Catalog source '{source_id}' is missing its 'priority'.\")\n        if isinstance(priority, bool) or not isinstance(priority, (int, str)):\n            raise BundlerError(\n                f\"Catalog source '{source_id}' has a non-integer priority: {priority!r}.\"\n            )\n        try:\n            priority_int = int(priority)\n        except (TypeError, ValueError):\n            raise BundlerError(\n                f\"Catalog source '{source_id}' has a non-integer priority: {priority!r}.\"\n            ) from None\n        return cls(\n            id=source_id,\n            url=url,","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/bundler/models/catalog.py#L57-L93","documentation":"Raised by CatalogSource.from_dict() when a source mapping has no 'id' key or an id that is empty/whitespace after stripping. The id is the primary handle for overriding built-ins and deduplicating sources, so it is mandatory for every configured source.","triggerScenarios":"A config entry like {\"url\": ..., \"priority\": ...} with no id; an id of \"\" or \"   \"; a YAML entry where 'id' was accidentally nested under another key or misspelled (e.g. 'name').","commonSituations":"Hand-written config entries that assume the id is derived from the url (only add_source() does derivation, not from_dict); key typos; copy-paste between different config formats.","solutions":["Add a non-empty 'id' string to the source mapping.","If creating sources programmatically, prefer add_source() which derives the id from the url when omitted.","Check for misspelled keys like 'source_id' or 'name' where 'id' was intended."],"exampleFix":"# before\n{\"url\": \"https://example.com/c.json\", \"priority\": 5, \"install_policy\": \"install-allowed\"}\n\n# after\n{\"id\": \"example\", \"url\": \"https://example.com/c.json\", \"priority\": 5, \"install_policy\": \"install-allowed\"}","handlingStrategy":"validation","validationCode":"def validate_source_entry(entry: dict) -> None:\n    if not str(entry.get(\"id\", \"\")).strip():\n        raise ValueError(\"source entry missing non-empty 'id'\")\n    CatalogSource.from_dict(entry, Scope.PROJECT)","typeGuard":"def has_source_id(entry: dict) -> bool:\n    return isinstance(entry, dict) and bool(str(entry.get(\"id\", \"\")).strip())","tryCatchPattern":"try:\n    CatalogSource.from_dict(entry, Scope.PROJECT)\nexcept BundlerError as e:\n    if \"missing its 'id'\" in str(e):\n        entry[\"id\"] = derive_id(entry[\"url\"])  # repair then retry","preventionTips":["Always create sources via add_source(), which derives ids from urls.","Include a non-empty id in every config entry.","Watch for key misspellings like 'name' or 'source_id'."],"tags":["bundler","catalog","validation","missing-field"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}