github/spec-kit · error · BundlerError

Catalog entry for '{key}' is missing its 'id' field.

Error message

Catalog entry for '{key}' is missing its 'id' field.

What it means

Raised by load_catalog_payload() when a bundles-map entry parses but its own 'id' field is missing or blank. The enclosing key is the authoritative bundle id used by search/resolve/install, and an entry without its own id cannot be confirmed to agree with that key, so a malformed or malicious catalog cannot list an id that resolves to a different (or no) bundle.

Source

Thrown at src/specify_cli/bundler/models/catalog.py:230


def load_catalog_payload(data: Any) -> dict[str, CatalogEntry]:
    """Parse a catalog JSON payload into ``{bundle_id: CatalogEntry}``."""
    if not isinstance(data, dict):
        raise BundlerError("Catalog payload must be a JSON object.")
    bundles_raw = data.get("bundles")
    if not isinstance(bundles_raw, dict):
        raise BundlerError("Catalog payload is missing a 'bundles' object.")
    entries: dict[str, CatalogEntry] = {}
    for bundle_id, entry_raw in bundles_raw.items():
        key = str(bundle_id)
        entry = CatalogEntry.from_dict(entry_raw)
        # The enclosing key is the authoritative bundle id used by
        # search/resolve/install. Reject entries whose own ``id`` is missing or
        # disagrees with the key, so a malformed or malicious catalog can't list
        # an id that resolves to a different (or no) bundle.
        if not entry.id:
            raise BundlerError(
                f"Catalog entry for '{key}' is missing its 'id' field."
            )
        if entry.id != key:
            raise BundlerError(
                f"Catalog entry id mismatch: key '{key}' != entry id "
                f"'{entry.id}'."
            )
        entries[key] = entry
    return entries


def load_source_stack(project_root: Path, user_config_dir: Path | None = None) -> list[CatalogSource]:
    """Build the effective, priority-sorted source stack (project > user > built-in).

    A source id present at a higher-precedence scope overrides the same id at a
    lower scope. The built-in default stack is always the fallback.
    """
    by_id: dict[str, CatalogSource] = {}

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Give every entry an 'id' field exactly equal to its enclosing bundles key.
  2. Fix catalog generators to emit the id inside each entry as well as using it as the key.
  3. Validate catalogs in CI with a small script that checks entry['id'] presence and key equality.

Example fix

# before (catalog JSON)
{"bundles": {"my-bundle": {"name": "My Bundle", "version": "1.0.0"}}}

# after (catalog JSON)
{"bundles": {"my-bundle": {"id": "my-bundle", "name": "My Bundle", "version": "1.0.0"}}}
Defensive patterns

Strategy: validation

Validate before calling

for key, raw in payload["bundles"].items():
    if not str(raw.get("id", "")).strip():
        raise ValueError(f"bundle '{key}' entry missing its 'id' field")
load_catalog_payload(payload)

Type guard

def entry_has_id(raw: object) -> bool:
    return isinstance(raw, dict) and bool(str(raw.get("id", "")).strip())

Try / catch

try:
    load_catalog_payload(data)
except BundlerError as e:
    if "missing its 'id' field" in str(e):
        report_malformed_catalog_entry(e)  # upstream fix required

Prevention

When it happens

Trigger: A catalog entry object like {"name": "X", "version": "1"} with no id; "id": "" or whitespace; id key misspelled ("bundle_id", "slug").

Common situations: Hand-authored catalogs assuming the key alone suffices; generators that strip id fields; schema drift after renaming.

Related errors


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