github/spec-kit · error · PresetError
Invalid preset catalog format from {url}: 'presets' must be
Error message
Invalid preset catalog format from {url}: 'presets' must be a JSON object What it means
The catalog's top-level `presets` key exists but is not a JSON object — the schema requires presets to be a mapping keyed by preset id, not an array. This is a distinct, later check from the missing-keys case so the author knows exactly what shape to fix.
Source
Thrown at src/specify_cli/presets/__init__.py:4255
catalog_data: Parsed JSON payload from the catalog source.
url: Source URL — used in the error message so the user can
tell which catalog in a multi-catalog stack is malformed.
Raises:
PresetError: If the payload's shape is invalid.
"""
if not isinstance(catalog_data, dict):
raise PresetError(
f"Invalid preset catalog format from {url}: "
"expected a JSON object"
)
if (
"schema_version" not in catalog_data
or "presets" not in catalog_data
):
raise PresetError(f"Invalid preset catalog format from {url}")
if not isinstance(catalog_data.get("presets"), dict):
raise PresetError(
f"Invalid preset catalog format from {url}: "
"'presets' must be a JSON object"
)
def _load_catalog_config(self, config_path: Path) -> Optional[List[PresetCatalogEntry]]:
"""Load catalog stack configuration from a YAML file.
Args:
config_path: Path to preset-catalogs.yml
Returns:
Ordered list of PresetCatalogEntry objects, or None if file
doesn't exist or contains no valid catalog entries.
Raises:
PresetValidationError: If any catalog entry has an invalid URL,
the file cannot be parsed, or a priority value is invalid.
"""View on GitHub (pinned to bf88c9f9a8)
Solutions
- Convert presets to an object keyed by preset id: {"presets": {"my-preset": {...}}} instead of an array.
- If publishing via a script, dump a dict (yaml/json safe_dump of a mapping), not a list.
Example fix
# before
{
"schema_version": 1,
"presets": [ {"id": "my-preset", "name": "My Preset"} ]
}
# after
{
"schema_version": 1,
"presets": {
"my-preset": {"name": "My Preset", "description": "..."}
}
} Defensive patterns
Strategy: type-guard
Validate before calling
data = json.load(urllib.request.urlopen(url))
assert isinstance(data.get("presets"), dict), "catalog 'presets' must be an object keyed by preset id" Type guard
def presets_is_mapping(payload: dict) -> bool:
return isinstance(payload.get("presets"), dict) Try / catch
try:
manager.refresh_catalogs()
except PresetError as e:
if "'presets' must be a JSON object" in str(e):
# convert the array to an id-keyed object and republish the catalog
... Prevention
- "presets" is a map of preset id -> entry, never an array.
- When generating catalogs from scripts, serialize a dict for presets.
- Check the whole shape (object root, schema_version, presets object) in one CI validation step.
When it happens
Trigger: A catalog document with `"presets": [ ... ]` (a JSON array of preset entries), or presets as a string/number. schema_version is present and correct, so only the presets shape is wrong.
Common situations: Authoring a catalog as a list because that feels natural, or converting from a format where presets were array-valued.
Related errors
- Invalid preset catalog format from {url}
- Invalid catalog format from {url}
- Invalid preset catalog format from {url}: expected a JSON ob
- auth.json must contain a 'providers' array
- providers[{i}]: must be a JSON object
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/b5720f2d5a625cfc.
Report an issue: GitHub.