github/spec-kit · error · PresetValidationError
Invalid catalog config: 'catalogs' must be a list, got {type
Error message
Invalid catalog config: 'catalogs' must be a list, got {type(catalogs_data).__name__} What it means
The catalog config's 'catalogs' key exists but its value is not a list. The loader requires 'catalogs' to be a sequence of entry mappings; a dict, string, or scalar triggers PresetValidationError.
Source
Thrown at src/specify_cli/presets/__init__.py:4290
the file cannot be parsed, or a priority value is invalid.
"""
if not config_path.exists():
return None
try:
data = yaml.safe_load(config_path.read_text(encoding="utf-8")) or {}
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 catalogView on GitHub (pinned to bf88c9f9a8)
Solutions
- Make the 'catalogs' value a YAML block sequence: 'catalogs:' followed by '- url: ...' items
- If a single catalog is needed, still keep it as a one-element list
- Run the config through a YAML linter to confirm type(catalogs) is list
Example fix
# before catalogs: url: https://example.com/catalog.json # after catalogs: - url: https://example.com/catalog.json
Defensive patterns
Strategy: type-guard
Validate before calling
data = yaml.safe_load(text)
catalogs = data.get("catalogs", []) if isinstance(data, dict) else []
if catalogs and not isinstance(catalogs, list):
raise ValueError("'catalogs' must be a list") Type guard
def has_valid_catalogs_list(data: dict) -> bool:
c = data.get("catalogs")
return c is None or isinstance(c, list) Try / catch
except PresetValidationError as e:
if "'catalogs' must be a list" in str(e):
# rewrite config to block-sequence form or flag entry to user
... Prevention
- Use block-sequence style for catalogs entries; avoid flow maps
- Add a config schema check (root mapping, catalogs is list of mappings) to setup tooling
When it happens
Trigger: Writing 'catalogs: {url: ...}' (a mapping), 'catalogs: https://...' (a scalar string), or 'catalogs:' followed by nested non-list content in the catalog config file.
Common situations: User copies a single-entry template with inline-map style instead of block-sequence style; YAML auto-parses what looked like a list into a scalar (e.g. an unquoted URL with special characters).
Related errors
- Invalid catalog config {config_path}: expected a mapping at
- Invalid catalog entry at index {idx}: expected a mapping, go
- 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/d3de9855d44c16b9.
Report an issue: GitHub.