github/spec-kit · error · ExtensionError
Failed to fetch catalog from {catalog_url}: {e}
Error message
Failed to fetch catalog from {catalog_url}: {e} What it means
Legacy/single-catalog fetch path (the one that maintains cache_metadata_file): urllib raised URLError while retrieving catalog_url, and the error is re-raised as an ExtensionError naming the URL. Unlike the multi-catalog merge path, this single-URL fetcher fails hard on the first network error.
Source
Thrown at src/specify_cli/extensions/__init__.py:4119
self.cache_file.write_text(
json.dumps(catalog_data, indent=2), encoding="utf-8"
)
# Save cache metadata
metadata = {
"cached_at": datetime.now(timezone.utc).isoformat(),
"catalog_url": catalog_url,
}
self.cache_metadata_file.write_text(
json.dumps(metadata, indent=2), encoding="utf-8"
)
except OSError:
pass # Cache is best-effort; proceed with fetched data
return catalog_data
except urllib.error.URLError as e:
raise ExtensionError(f"Failed to fetch catalog from {catalog_url}: {e}")
except json.JSONDecodeError as e:
raise ExtensionError(f"Invalid JSON in catalog: {e}")
def search(
self,
query: Optional[str] = None,
tag: Optional[str] = None,
author: Optional[str] = None,
verified_only: bool = False,
) -> List[Dict[str, Any]]:
"""Search catalog for extensions across all active catalogs.
Args:
query: Search query (searches name, description, tags)
tag: Filter by specific tag
author: Filter by author name
verified_only: If True, show only verified extensions
View on GitHub (pinned to bf88c9f9a8)
Solutions
- Verify reachability of the exact URL: `curl -vf <catalog_url>`
- Fix the catalog_url configuration (scheme, host, port) or point it at a reachable mirror
- Configure proxy env vars (HTTPS_PROXY/HTTP_PROXY) if egress requires them
- Handle the ExtensionError in the caller to degrade gracefully (empty result + warning) when the catalog is optional
Defensive patterns
Strategy: retry
Validate before calling
import urllib.request
def catalog_url_ok(url: str) -> bool:
try:
req = urllib.request.Request(url, method='HEAD')
with urllib.request.urlopen(req, timeout=5) as r:
return r.status == 200
except OSError:
return False Try / catch
from specify_cli.extensions import ExtensionError
import time
for attempt in range(3):
try:
catalog = catalog_api.fetch()
break
except ExtensionError as e:
if 'Failed to fetch catalog from' in str(e) and attempt < 2:
time.sleep(2 ** attempt)
continue
raise Prevention
- Add a reachability check before batch operations that hit the catalog
- Monitor self-hosted catalog servers and keep a mirror URL on standby
When it happens
Trigger: Calling the catalog search/fetch API that goes through this single-URL path with an unreachable catalog_url — DNS failure, connection refused, TLS handshake failure, or an HTTP error surfaced as URLError.
Common situations: Private/self-hosted catalog server down for maintenance; CI runner without network; corporate proxy blocking the catalog host; wrong scheme or port in the URL.
Related errors
- Failed to fetch catalog from {entry.url}: {e}
- Could not query extension catalog: {catalog_error}
- Invalid JSON in catalog from {entry.url}: {e}
- Failed to fetch any extension catalog
- Invalid JSON in catalog: {e}
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/b9b1b7c543905acf.
Report an issue: GitHub.