github/spec-kit · error · BundlerError
A catalog source is missing its 'id'.
Error message
A catalog source is missing its 'id'.
What it means
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.
Source
Thrown at src/specify_cli/bundler/models/catalog.py:75
class CatalogSource:
id: str
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,View on GitHub (pinned to bf88c9f9a8)
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.
Example fix
# before
{"url": "https://example.com/c.json", "priority": 5, "install_policy": "install-allowed"}
# after
{"id": "example", "url": "https://example.com/c.json", "priority": 5, "install_policy": "install-allowed"} Defensive patterns
Strategy: validation
Validate before calling
def validate_source_entry(entry: dict) -> None:
if not str(entry.get("id", "")).strip():
raise ValueError("source entry missing non-empty 'id'")
CatalogSource.from_dict(entry, Scope.PROJECT) Type guard
def has_source_id(entry: dict) -> bool:
return isinstance(entry, dict) and bool(str(entry.get("id", "")).strip()) Try / catch
try:
CatalogSource.from_dict(entry, Scope.PROJECT)
except BundlerError as e:
if "missing its 'id'" in str(e):
entry["id"] = derive_id(entry["url"]) # repair then retry Prevention
- 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'.
When it happens
Trigger: 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').
Common situations: 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.
Related errors
- Catalog source '{source_id}' is missing its 'url'.
- 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/157b8c9ade3e6293.
Report an issue: GitHub.