github/spec-kit · error · ExtensionError

Failed to fetch any extension catalog

Error message

Failed to fetch any extension catalog

What it means

Raised by _get_merged_extensions when there is at least one active catalog configured but every single fetch attempt failed (any_success is false). This is the total-failure sentinel: individual catalog failures are normally tolerated and skipped, so seeing this means the entire catalog stack — custom and built-in — was unreachable or malformed in a way that produced no successful fetch.

Source

Thrown at src/specify_cli/extensions/__init__.py:3987

                # that ``catalog_data["extensions"]`` is a mapping, but it
                # does not (and should not) validate every entry shape there
                # — one malformed entry shouldn't poison an otherwise valid
                # catalog. Skip non-mapping entries here so a payload like
                # ``{"extensions": {"foo": [], "bar": {...}}}`` still merges
                # the valid entries without crashing on ``**ext_data``.
                # Mirrors ``integrations/catalog.py:245``.
                if not isinstance(ext_data, dict):
                    continue
                if ext_id not in merged:  # Higher-priority catalog wins
                    merged[ext_id] = {
                        **ext_data,
                        "id": ext_id,
                        "_catalog_name": catalog_entry.name,
                        "_install_allowed": catalog_entry.install_allowed,
                    }

        if not any_success and active_catalogs:
            raise ExtensionError("Failed to fetch any extension catalog")

        return list(merged.values())

    def is_cache_valid(self) -> bool:
        """Check if cached catalog is still valid.

        Returns ``False`` for any read/decoding failure on the metadata
        file (missing fields, malformed JSON, permissions / disk errors,
        wrong text encoding) so callers fall through to a network refetch
        instead of crashing. Treating cache validity as best-effort
        matches the contract used by the per-URL cache check below.

        Returns:
            True if cache exists and is within cache duration
        """
        if not self.cache_file.exists() or not self.cache_metadata_file.exists():
            return False

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Restore network access or allow egress to the catalog hosts, then retry
  2. If SPECKIT_CATALOG_URL is set, verify it: `echo $SPECKIT_CATALOG_URL` and `curl -f <url>`; unset it to fall back to defaults once reachable
  3. Mirror the catalog locally and point the env var at http://localhost (localhost HTTP is permitted)
  4. Check whether cached catalog data can bridge the outage — if a valid cache exists, investigate why the refetch path was forced (force_refresh)

Example fix

# before: env var forces a single dead catalog
export SPECKIT_CATALOG_URL=https://internal.example.invalid/catalog.json

# after: local mirror while offline, or unset to use defaults
python -m http.server 8080 --directory /path/to/mirror &
export SPECKIT_CATALOG_URL=http://localhost:8080/catalog.json
Defensive patterns

Strategy: fallback

Validate before calling

import socket, urllib.parse

def can_reach_any(hosts: list[str]) -> bool:
    for h in hosts:
        try:
            socket.create_connection((h, 443), timeout=3).close()
            return True
        except OSError:
            continue
    return False

Try / catch

from specify_cli.extensions import ExtensionError

try:
    merged = manager._get_merged_extensions()
except ExtensionError as e:
    if str(e) == 'Failed to fetch any extension catalog':
        merged = []  # offline fallback: degrade to empty list with a warning
        ...

Prevention

When it happens

Trigger: Running `specify extension list`/`search` (or anything calling _get_merged_extensions) while completely offline with the default catalog stack active, or with a custom SPECKIT_CATALOG_URL / catalog config where every configured URL fails.

Common situations: Air-gapped or CI environments with no network egress; SPECKIT_CATALOG_URL pointing at a dead server (it replaces all defaults, so its failure alone triggers this); system-level DNS outage.

Related errors


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