github/spec-kit · error · PresetError

Invalid preset catalog format from {url}: expected a JSON ob

Error message

Invalid preset catalog format from {url}: expected a JSON object

What it means

The fetched catalog payload parsed as JSON but was not a JSON object at the top level (e.g. a list or a bare string/number). The catalog schema requires a top-level object with schema_version and presets keys; the error names the source URL so you can tell which catalog in a multi-catalog stack is malformed.

Source

Thrown at src/specify_cli/presets/__init__.py:4245

        ``{"presets": []}`` or ``{"presets": null}`` slip through here and
        then crash with ``AttributeError: 'list' object has no attribute
        'items'`` deep inside ``_get_merged_packs``. The sibling
        integration catalog reader already guards both the root object and
        the nested mapping (see ``integrations/catalog.py``); the preset
        catalog must stay consistent so a malformed payload surfaces as
        the user-facing ``Invalid preset catalog format`` error instead of
        a raw Python traceback.

        Args:
            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:

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Point the catalog URL at a catalog document whose top level is an object: {"schema_version": ..., "presets": {...}}.
  2. Verify with: `curl -s <url> | python -m json.tool | head` — the first token must be `{`.
  3. If a proxy rewrites responses, bypass or fix it for this URL.

Example fix

# before (served catalog.json)
[ {"id": "my-preset", ...} ]

# after
{
  "schema_version": 1,
  "presets": { "my-preset": { ... } }
}
Defensive patterns

Strategy: type-guard

Validate before calling

import json, urllib.request
data = json.load(urllib.request.urlopen(url))
assert isinstance(data, dict), f"catalog {url} is not a JSON object"

Type guard

def is_catalog_object(payload) -> bool:
    return isinstance(payload, dict)

Try / catch

try:
    manager.refresh_catalogs()
except PresetError as e:
    if "expected a JSON object" in str(e):
        # the URL serves an array/string; point it at a proper catalog document
        ...

Prevention

When it happens

Trigger: A catalog URL returning a JSON array of presets, or a proxy/gateway returning a JSON-encoded error string. Raised in the shape-validation step after json parsing, before any preset entry is read.

Common situations: Pointing the catalog URL at a raw GitHub API listing (returns an array), an index of JSON files rather than the catalog document itself, or a CDN serving a wrapped payload.

Related errors


AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14). Data as JSON: /api/errors/a89f6b579cfc6427. Report an issue: GitHub.