github/spec-kit · error · BundlerError

Malformed catalog config at {path}: expected a mapping at th

Error message

Malformed catalog config at {path}: expected a mapping at the top level, got {type(data).__name__}.

What it means

Raised by the reader of `.specify/bundle-catalogs.yml` when the parsed YAML top level is not a mapping. The file shape is `{schema_version, catalogs: [...]}`; a top-level list, scalar, or explicit null is treated as corrupt, consistently with the other reader (`models/catalog._merge_config`). The message names the actual type received.

Source

Thrown at src/specify_cli/bundler/commands_impl/catalog_config.py:49

def _config_path(project_root: Path) -> Path:
    return Path(project_root) / ".specify" / CONFIG_FILENAME


def _read(project_root: Path) -> list[dict]:
    # Confine the read (parity with the write path's within= guard): refuse to
    # follow a symlinked or traversal-escaping .specify that resolves outside
    # project_root.
    path = ensure_within(project_root, _config_path(project_root))
    if not path.exists():
        return []
    # ``load_yaml`` returns ``{}`` only for an empty document and the raw parse
    # otherwise, so a non-mapping top level — a falsy ``[]``/``false``/``0``/``''``
    # or an explicit null (``load_yaml`` -> ``None``) — is caught by the isinstance
    # guard below and raised like a truthy one, staying consistent with the other
    # reader of this file (models/catalog._merge_config).
    data = load_yaml(path)
    if not isinstance(data, dict):
        raise BundlerError(
            f"Malformed catalog config at {path}: expected a mapping at the top "
            f"level, got {type(data).__name__}."
        )
    schema_version = data.get("schema_version")
    if schema_version is not None and (
        str(schema_version).strip().split(".")[0]
        != CONFIG_SCHEMA_VERSION.split(".")[0]
    ):
        raise BundlerError(
            f"Unsupported catalog config schema version "
            f"'{str(schema_version).strip()}' at {path}; this Spec Kit "
            f"understands version {CONFIG_SCHEMA_VERSION}. The file may have been "
            "written by a newer version or is corrupt."
        )
    catalogs = data.get("catalogs")
    if catalogs is None:
        return []
    if not isinstance(catalogs, list):

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Wrap the contents in a top-level mapping with `schema_version` and `catalogs` keys
  2. If the file is hopelessly mangled, delete it — built-in defaults still work and it will be recreated
  3. Prefer `specify bundle catalog add ...` (add_source) over hand-editing

Example fix

# before (root is a list)
- id: community
  url: https://example.com/c.json

# after
schema_version: "1.0"
catalogs:
  - id: community
    url: https://example.com/c.json
Defensive patterns

Strategy: try-catch

Validate before calling

import yaml

def looks_like_catalog_config(path) -> bool:
    try:
        with open(path) as f:
            data = yaml.safe_load(f)
    except yaml.YAMLError:
        return False
    return data is None or isinstance(data, dict)

Type guard

def is_catalog_config_mapping(data: object) -> bool:
    return data is None or isinstance(data, dict)

Try / catch

from specify_cli.bundler import BundlerError

try:
    catalogs = read_catalog_config(project_root)
except BundlerError as exc:
    if "expected a mapping at the top level" in str(exc):
        # back up the corrupt file, regenerate from `specify bundle catalog add`
        raise
    raise

Prevention

When it happens

Trigger: `bundle-catalogs.yml` whose root node is a list (e.g. a bare `- id: ...` sequence), a scalar/string, or a `null` document — `load_yaml` returns `{}` only for an empty document, so anything else non-dict lands here.

Common situations: Hand-editing the file and dropping the outer mapping; pasting a catalog entry list directly; a truncated file after an interrupted write; converting from another YAML format that is a list at the root.

Understand the failure class

Related errors


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