github/spec-kit · error · BundlerError
Catalog source '{source_id}' is missing its 'url'.
Error message
Catalog source '{source_id}' is missing its 'url'. What it means
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.
Source
Thrown at src/specify_cli/bundler/models/catalog.py:77
url: str
priority: int
install_policy: InstallPolicy
scope: Scope = Scope.PROJECT
@property
def install_allowed(self) -> bool:
return self.install_policy is InstallPolicy.INSTALL_ALLOWED
@classmethod
def from_dict(cls, data: Any, scope: Scope) -> "CatalogSource":
if not isinstance(data, dict):
raise BundlerError("Each catalog source must be a mapping.")
source_id = str(data.get("id", "")).strip()
url = str(data.get("url", "")).strip()
if not source_id:
raise BundlerError("A catalog source is missing its 'id'.")
if not url:
raise BundlerError(f"Catalog source '{source_id}' is missing its 'url'.")
priority = data.get("priority")
if priority is None:
raise BundlerError(f"Catalog source '{source_id}' is missing its 'priority'.")
if isinstance(priority, bool) or not isinstance(priority, (int, str)):
raise BundlerError(
f"Catalog source '{source_id}' has a non-integer priority: {priority!r}."
)
try:
priority_int = int(priority)
except (TypeError, ValueError):
raise BundlerError(
f"Catalog source '{source_id}' has a non-integer priority: {priority!r}."
) from None
return cls(
id=source_id,
url=url,
priority=priority_int,
install_policy=InstallPolicy.parse(data.get("install_policy")),View on GitHub (pinned to bf88c9f9a8)
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>', ...).
Example fix
# before
{"id": "example", "priority": 5, "install_policy": "install-allowed"}
# after
{"id": "example", "url": "https://example.com/catalog.json", "priority": 5, "install_policy": "install-allowed"} Defensive patterns
Strategy: validation
Validate before calling
if not str(entry.get("url", "")).strip():
raise ValueError("source entry missing non-empty 'url'")
CatalogSource.from_dict(entry, Scope.PROJECT) Type guard
def has_source_url(entry: dict) -> bool:
return isinstance(entry, dict) and bool(str(entry.get("url", "")).strip()) Try / catch
try:
CatalogSource.from_dict(entry, Scope.PROJECT)
except BundlerError as e:
if "missing its 'url'" in str(e):
flag_entry_for_human_review(entry) Prevention
- 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.
When it happens
Trigger: 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.
Common situations: Partial hand-edits; templating that drops the url when a variable is empty; converting configs between tools that use different key names.
Related errors
- A catalog source is missing its 'id'.
- Catalog source '{source_id}' is missing its 'priority'.
- Catalog payload is missing a 'bundles' object.
- Malformed catalog config at {path}: expected a mapping at th
- Malformed catalog config at {path}: 'catalogs' must be a lis
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/123b06647cb6d7bc.
Report an issue: GitHub.