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
- Restore network access or allow egress to the catalog hosts, then retry
- 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
- Mirror the catalog locally and point the env var at http://localhost (localhost HTTP is permitted)
- 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
- Ensure CI has egress to catalog hosts or serves a localhost mirror
- If overriding with SPECKIT_CATALOG_URL, remember it replaces ALL defaults — its failure alone triggers total failure
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
- Could not query extension catalog: {catalog_error}
- Failed to fetch catalog from {entry.url}: {e}
- Invalid JSON in catalog from {entry.url}: {e}
- Failed to fetch catalog from {catalog_url}: {e}
- Invalid JSON in catalog: {e}
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/fc0aa1a3367831d5.
Report an issue: GitHub.