github/spec-kit · error · BundlerError
Catalog entry '{entry_id or '<unknown>'}': 'requires' must b
Error message
Catalog entry '{entry_id or '<unknown>'}': 'requires' must be a mapping when present. What it means
Raised by CatalogEntry.from_dict() when an entry's 'requires' field is present but is not a mapping. 'requires' declares capability/version prerequisites as an object (e.g. {"speckit": ">=1.0"}); the code deliberately avoids `or {}` because that would silently coerce falsy non-mappings (0, '', False, []) into 'no requirements', accepting a corrupt entry.
Source
Thrown at src/specify_cli/bundler/models/catalog.py:168
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 "
"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(),View on GitHub (pinned to bf88c9f9a8)
Solutions
- Express 'requires' as an object mapping requirement names to constraints: {"speckit": ">=1.0"}.
- If no requirements, omit the key or set it to null — do not use an empty string or false.
- Check the catalog schema documentation for the expected mapping shape.
Example fix
# before (catalog JSON)
"requires": ["speckit>=1.0"]
# after (catalog JSON)
"requires": {"speckit": ">=1.0"} Defensive patterns
Strategy: type-guard
Validate before calling
requires = entry.get("requires")
if requires is not None and not isinstance(requires, dict):
raise ValueError("'requires' must be a mapping when present") Type guard
def is_valid_requires(value: object) -> bool:
return value is None or isinstance(value, dict) Try / catch
try:
CatalogEntry.from_dict(entry_raw)
except BundlerError as e:
if "'requires' must be a mapping" in str(e):
entry_raw.pop("requires", None) # drop rather than trust a malformed constraint Prevention
- Write requires as {"name": "constraint"} objects, never lists or strings.
- Omit requires when there are no prerequisites.
- Validate published catalogs with a JSON schema before serving them.
When it happens
Trigger: A catalog entry with "requires": ["speckit>=1.0"] (list) or "requires": ">=1.0" (string); requires: 0 or false in hand-written YAML; requires: null is fine (treated as absent), anything else raises.
Common situations: Catalog authors more comfortable with arrays writing dependency lists; converting a requirements.txt-style syntax into catalogs; schema drift between catalog versions.
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>'}': '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/0068270fd690b078.
Report an issue: GitHub.