{"record":{"id":"123b06647cb6d7bc","repo":"github/spec-kit","slug":"catalog-source-source-id-is-missing-its-url","errorCode":null,"errorMessage":"Catalog source '{source_id}' is missing its 'url'.","messagePattern":"Catalog source '(.+?)' is missing its 'url'\\.","errorType":"exception","errorClass":"BundlerError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/bundler/models/catalog.py","lineNumber":77,"sourceCode":"    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,\n            priority=priority_int,\n            install_policy=InstallPolicy.parse(data.get(\"install_policy\")),","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/bundler/models/catalog.py#L59-L95","documentation":"Raised by CatalogSource.from_dict() when a source mapping has an 'id' but no 'url' (or a blank one). Every catalog source must point at a fetchable location — an https url, an http url for localhost, or a local file path — because the stack resolves bundles by fetching that url.","triggerScenarios":"A config entry with id/priority/policy but no url; url key misspelled ('link', 'href', 'src'); url value of \"\" or whitespace; hand-edited file where the url line was deleted.","commonSituations":"Partial hand-edits; templating that drops the url when a variable is empty; converting configs between tools that use different key names.","solutions":["Add the 'url' key with the catalog's https url or a local file path.","Verify the key is exactly 'url' and the value is non-blank.","Regenerate the entry via add_source(root, '<id>', '<url>', ...)."],"exampleFix":"# before\n{\"id\": \"example\", \"priority\": 5, \"install_policy\": \"install-allowed\"}\n\n# after\n{\"id\": \"example\", \"url\": \"https://example.com/catalog.json\", \"priority\": 5, \"install_policy\": \"install-allowed\"}","handlingStrategy":"validation","validationCode":"if not str(entry.get(\"url\", \"\")).strip():\n    raise ValueError(\"source entry missing non-empty 'url'\")\nCatalogSource.from_dict(entry, Scope.PROJECT)","typeGuard":"def has_source_url(entry: dict) -> bool:\n    return isinstance(entry, dict) and bool(str(entry.get(\"url\", \"\")).strip())","tryCatchPattern":"try:\n    CatalogSource.from_dict(entry, Scope.PROJECT)\nexcept BundlerError as e:\n    if \"missing its 'url'\" in str(e):\n        flag_entry_for_human_review(entry)","preventionTips":["Templating that renders config should fail loudly on empty url variables, not emit blank values.","Spell the key exactly 'url'.","Validate entries against the documented schema before loading."],"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"}