github/spec-kit · error · BundlerError
Each catalog source must be a mapping.
Error message
Each catalog source must be a mapping.
What it means
Raised by CatalogSource.from_dict() when a single catalog source entry in configuration is not a JSON/YAML object (dict). Sources are loaded from project and user config files whose top-level shape must be a list of mappings; any scalar, list, or null element fails this guard before field validation begins.
Source
Thrown at src/specify_cli/bundler/models/catalog.py:71
)
@dataclass(frozen=True)
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}."View on GitHub (pinned to bf88c9f9a8)
Solutions
- Make each element of the sources list an object with at least id, url, priority, and install_policy keys.
- Validate the config with a JSON linter or json.load plus an isinstance(item, dict) check before relying on it.
- Regenerate the config through add_source() instead of editing the file by hand.
Example fix
# before (config file)
[{"id": "a", "url": "https://x/c.json", "priority": 5, "install_policy": "install-allowed"}, "https://y/c.json"]
# after
[{"id": "a", "url": "https://x/c.json", "priority": 5, "install_policy": "install-allowed"},
{"id": "b", "url": "https://y/c.json", "priority": 5, "install_policy": "install-allowed"}] Defensive patterns
Strategy: type-guard
Validate before calling
import json
data = json.loads(config_text)
assert isinstance(data, list), "catalog config must be a list"
for item in data:
if not isinstance(item, dict):
raise ValueError(f"catalog source entry is not an object: {item!r}") Type guard
def is_source_mapping(item: object) -> bool:
return isinstance(item, dict) Try / catch
try:
CatalogSource.from_dict(item, Scope.PROJECT)
except BundlerError as e:
log_config_error(config_path, e) # point the user at the offending entry Prevention
- Generate catalog config only through add_source(), never by hand-writing JSON.
- Lint config files after manual edits.
- Reject non-object entries in your own config loaders before they reach this library.
When it happens
Trigger: A catalog config file whose 'sources'-style list contains a bare string (e.g. a url) instead of an object; a YAML dash-item that parsed as a scalar; a null entry from a trailing comma or malformed JSON merge.
Common situations: Hand-editing the catalog config and writing just a url per line; JSON produced by string concatenation instead of a serializer; config templating that renders an empty placeholder value.
Related errors
- Malformed catalog config at {path}: expected a mapping at th
- Malformed catalog config at {path}: 'catalogs' must be a lis
- Malformed catalog config at {path}: each catalog entry must
- A catalog url is required.
- Invalid catalog url: '{url}'.
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/07773b46bd0ff961.
Report an issue: GitHub.