github/spec-kit · error · BundlerError
Each catalog entry must be a mapping.
Error message
Each catalog entry must be a mapping.
What it means
Raised by CatalogEntry.from_dict() when an element under a catalog's 'bundles' object is not a mapping. Each value in the bundles map must itself be a JSON object describing the bundle; scalars, lists, or nulls fail this guard before any field checks run.
Source
Thrown at src/specify_cli/bundler/models/catalog.py:159
role: str
description: str
author: str
license: str
download_url: str
requires_speckit_version: str
sha256: str | None = None
provides: dict[str, int] = field(default_factory=dict)
repository: str | None = None
tags: tuple[str, ...] = ()
verified: bool = False
# Resolution provenance (filled in by the catalog stack at lookup time):
source_id: str | None = None
source_policy: InstallPolicy | None = None
@classmethod
def from_dict(cls, data: Any) -> "CatalogEntry":
if not isinstance(data, dict):
raise BundlerError("Each catalog entry must be a mapping.")
entry_id = str(data.get("id", "")).strip()
# `or {}` would coerce a FALSY non-mapping (0, '', False, []) to {} before
# the isinstance guard, silently accepting a corrupt catalog entry; only
# an absent/None value means "not present".
requires = data.get("requires")
if requires is None:
requires = {}
elif not isinstance(requires, dict):
raise BundlerError(
f"Catalog entry '{entry_id or '<unknown>'}': 'requires' must be a "
"mapping when present."
)
provides_raw = data.get("provides")
if provides_raw is None:
provides_raw = {}
elif not isinstance(provides_raw, dict):
raise BundlerError(
f"Catalog entry '{entry_id or '<unknown>'}': 'provides' must be a "View on GitHub (pinned to bf88c9f9a8)
Solutions
- Make each bundles value a full entry object with at least id (matching the key), name, version, role, and the other expected fields.
- Fix the catalog generator to emit objects, not scalars.
- Validate fetched catalogs with a schema check before load_catalog_payload() in test harnesses.
Example fix
# before (catalog JSON)
{"bundles": {"my-bundle": "1.0.0"}}
# after (catalog JSON)
{"bundles": {"my-bundle": {"id": "my-bundle", "name": "My Bundle", "version": "1.0.0", "role": "command", "description": "", "author": "", "license": "", "download_url": "https://..."}}} Defensive patterns
Strategy: type-guard
Validate before calling
for key, raw in payload.get("bundles", {}).items():
if not isinstance(raw, dict):
raise ValueError(f"bundle '{key}' entry is not an object") Type guard
def is_entry_mapping(value: object) -> bool:
return isinstance(value, dict) Try / catch
try:
CatalogEntry.from_dict(entry_raw)
except BundlerError as e:
if "must be a mapping" in str(e):
skip_and_report_entry(bundle_id, e) Prevention
- Schema-check catalogs in your test harness: every bundles value must be an object.
- Generate catalogs with serializers, not string templates.
- Log the offending bundle key when validation fails so authors can fix it.
When it happens
Trigger: A catalog payload like {"bundles": {"my-bundle": "1.0.0"}} where the value is a bare version string; a null value from sparse JSON; a producer that inlined a list of files as the entry.
Common situations: Hand-authored catalogs misunderstanding the schema; catalogs generated by scripts mapping bundle ids to version strings; JSON merge leaving null placeholders.
Related errors
- Catalog entry '{entry_id}': 'tags' must be a list of strings
- Catalog entry '{entry_id}': 'verified' must be a boolean (tr
- Catalog entry '{entry_id or '<unknown>'}': 'requires' must b
- Catalog entry '{entry_id or '<unknown>'}': 'provides' must b
- Catalog payload must be a JSON object.
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/ca9d11b83d9c7a2c.
Report an issue: GitHub.