github/spec-kit · error · PresetValidationError
Invalid catalog entry at index {idx}: expected a mapping, go
Error message
Invalid catalog entry at index {idx}: expected a mapping, got {type(item).__name__} What it means
One element of the 'catalogs' list is not a mapping. Each catalog entry must be a dict with keys like url/name/priority/install_allowed; a string, number, or None element raises PresetValidationError with the offending index.
Source
Thrown at src/specify_cli/presets/__init__.py:4296
except (yaml.YAMLError, OSError, UnicodeError) as e:
raise PresetValidationError(
f"Failed to read catalog config {config_path}: {e}"
)
if not isinstance(data, dict):
raise PresetValidationError(
f"Invalid catalog config {config_path}: expected a mapping at root, got {type(data).__name__}"
)
catalogs_data = data.get("catalogs", [])
if not catalogs_data:
return None
if not isinstance(catalogs_data, list):
raise PresetValidationError(
f"Invalid catalog config: 'catalogs' must be a list, got {type(catalogs_data).__name__}"
)
entries: List[PresetCatalogEntry] = []
for idx, item in enumerate(catalogs_data):
if not isinstance(item, dict):
raise PresetValidationError(
f"Invalid catalog entry at index {idx}: expected a mapping, got {type(item).__name__}"
)
url = str(item.get("url", "")).strip()
if not url:
continue
self._validate_catalog_url(url)
raw_priority = item.get("priority", idx + 1)
# Reject bools explicitly: ``bool`` is a subclass of ``int`` so
# ``int(True)`` silently returns 1, which would let a YAML
# ``priority: true`` slip through as a valid priority of 1. The
# sibling integration-catalog reader in ``catalogs.py`` already
# guards this; mirror the check here so the three catalog
# validators stay consistent.
if isinstance(raw_priority, bool):
raise PresetValidationError(
f"Invalid priority for catalog '{item.get('name', idx + 1)}': "
f"expected integer, got {raw_priority!r}"
)View on GitHub (pinned to bf88c9f9a8)
Solutions
- Find the element at the reported index and convert it to a mapping: '- url: <value>'
- Remove empty or placeholder entries from the catalogs list
- Ensure every entry is '- key: value' block form, not a bare quoted string
Example fix
# before catalogs: - https://example.com/catalog.json # after catalogs: - url: https://example.com/catalog.json
Defensive patterns
Strategy: type-guard
Validate before calling
for i, item in enumerate(data["catalogs"]):
if not isinstance(item, dict):
raise ValueError(f"catalog entry {i} must be a mapping") Type guard
def is_catalog_entry_list(items: object) -> bool:
return isinstance(items, list) and all(isinstance(x, dict) for x in items) Try / catch
except PresetValidationError as e:
if "expected a mapping" in str(e):
# parse index from message, convert bare strings to {'url': s}
... Prevention
- Never put bare URL strings in the catalogs list; always '- url: ...'
- Validate each entry is a mapping before calling the loader
When it happens
Trigger: A catalogs list containing a bare string ('catalogs: [https://example.com]') or a null element; any element parsed as a scalar instead of a mapping.
Common situations: Flow-style lists of URL strings instead of list-of-maps; a dash followed by nothing (empty entry) in the block sequence; copy-paste leaving a stray scalar between entries.
Related errors
- Invalid catalog config {config_path}: expected a mapping at
- Invalid catalog config: 'catalogs' must be a list, got {type
- Malformed catalog config at {path}: expected a mapping at th
- Malformed catalog config at {config_path}: expected a mappin
- Malformed catalog config at {config_path}: 'catalogs' must b
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/bb6d26e985f95480.
Report an issue: GitHub.