github/spec-kit · error · BundlerError
Catalog entry '{entry_id or '<unknown>'}': 'provides' must b
Error message
Catalog entry '{entry_id or '<unknown>'}': 'provides' must be a mapping when present. What it means
Raised by CatalogEntry.from_dict() when an entry's 'provides' field is present but is not a mapping. 'provides' declares capabilities the bundle supplies with integer weights (e.g. {"lint": 1}); the same no-`or {}` discipline as 'requires' applies so corrupt falsy values cannot masquerade as 'provides nothing'.
Source
Thrown at src/specify_cli/bundler/models/catalog.py:176
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 "
"mapping when present."
)
return cls(
id=entry_id,
name=str(data.get("name", "")).strip(),
version=str(data.get("version", "")).strip(),
role=str(data.get("role", "")).strip(),
description=str(data.get("description", "")).strip(),
author=str(data.get("author", "")).strip(),
license=str(data.get("license", "")).strip(),
download_url=str(data.get("download_url", "")).strip(),
requires_speckit_version=str(requires.get("speckit_version", "")).strip(),
sha256=(
None
if data.get("sha256") is None
else str(data["sha256"]).strip()
),View on GitHub (pinned to bf88c9f9a8)
Solutions
- Express 'provides' as an object mapping capability names to integers: {"lint": 1}.
- If the bundle provides nothing declared, omit the key or set null.
- Align with the documented catalog schema before publishing.
Example fix
# before (catalog JSON)
"provides": ["lint"]
# after (catalog JSON)
"provides": {"lint": 1} Defensive patterns
Strategy: type-guard
Validate before calling
provides = entry.get("provides")
if provides is not None and not isinstance(provides, dict):
raise ValueError("'provides' must be a mapping when present") Type guard
def is_valid_provides(value: object) -> bool:
return value is None or isinstance(value, dict) Try / catch
try:
CatalogEntry.from_dict(entry_raw)
except BundlerError as e:
if "'provides' must be a mapping" in str(e):
entry_raw.pop("provides", None) # drop malformed capability claims Prevention
- Write provides as {"capability": weight} objects with integer weights.
- Omit provides when the bundle declares no capabilities.
- Keep 'requires' and 'provides' shapes symmetric in docs and generators to avoid confusion.
When it happens
Trigger: A catalog entry with "provides": "lint" or "provides": ["lint"]; provides: 0 or false; provides: null is fine (treated as absent).
Common situations: Catalog authors writing provides as a tag list; schema drift; hand-edited catalogs approximating the format from memory.
Related errors
- Catalog entry '{entry_id}': 'tags' must be a list of strings
- Catalog entry '{entry_id}': 'verified' must be a boolean (tr
- Each catalog entry must be a mapping.
- Catalog entry '{entry_id or '<unknown>'}': 'requires' 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/e561609ce9f37d79.
Report an issue: GitHub.