github/spec-kit · error · ExtensionError
Invalid JSON in catalog from {entry.url}: {e}
Error message
Invalid JSON in catalog from {entry.url}: {e} What it means
Raised when a catalog URL responds successfully but the body is not valid JSON, so json parsing raises JSONDecodeError. The message embeds the decode error and the source URL, distinguishing 'reachable but malformed' from the network-failure variant.
Source
Thrown at src/specify_cli/extensions/__init__.py:3926
cache_meta_file.write_text(
json.dumps(
{
"cached_at": datetime.now(timezone.utc).isoformat(),
"catalog_url": entry.url,
},
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 {entry.url}: {e}")
except json.JSONDecodeError as e:
raise ExtensionError(f"Invalid JSON in catalog from {entry.url}: {e}")
def _get_merged_extensions(
self, force_refresh: bool = False
) -> List[Dict[str, Any]]:
"""Fetch and merge extensions from all active catalogs.
Higher-priority (lower priority number) catalogs win on conflicts
(same extension id in two catalogs). Each extension dict is annotated with:
- _catalog_name: name of the source catalog
- _install_allowed: whether installation is allowed from this catalog
Catalogs that fail to fetch are skipped. Raises ExtensionError only if
ALL catalogs fail.
Args:
force_refresh: If True, bypass all caches
Returns:View on GitHub (pinned to bf88c9f9a8)
Solutions
- Fetch and inspect the raw body: `curl -s <url> | head -c 400` — look for HTML or error text
- Point the catalog entry at the raw JSON endpoint (e.g. raw.githubusercontent.com, not the blob page)
- Fix authentication so the catalog server returns JSON instead of a login redirect
- Re-serialize hand-edited JSON: `python -m json.tool catalog.json` to find the syntax error
Example fix
# before: catalog URL pointing at the GitHub web UI url: https://github.com/org/repo/blob/main/catalog.json # after: raw file endpoint url: https://raw.githubusercontent.com/org/repo/main/catalog.json
Defensive patterns
Strategy: validation
Validate before calling
import json, urllib.request
def fetch_valid_catalog_json(url: str) -> dict:
with urllib.request.urlopen(url, timeout=10) as r:
body = r.read()
return json.loads(body) # raises JSONDecodeError early, with body inspectable Try / catch
from specify_cli.extensions import ExtensionError
try:
...
except ExtensionError as e:
if 'Invalid JSON in catalog' in str(e):
# endpoint served HTML/empty; check auth and use the raw file URL Prevention
- Point catalog config at raw JSON endpoints, not UI or auth-wrapped pages
- json.tool-validate any hand-edited catalog before upload
When it happens
Trigger: Catalog URL returns HTML (error page, login page, SPA), an empty body, truncated JSON, or a BOM-prefixed document; json.loads fails inside the fetch for entry.url.
Common situations: Catalog URL behind auth redirecting to an HTML login page; reverse proxy returning a 200 HTML maintenance page; URL pointing at a human-readable web view of the JSON file rather than the raw file; encoding issues from hand-edited files.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Invalid JSON in catalog: {e}
- Could not query extension catalog: {catalog_error}
- Invalid catalog format from {url}: expected a JSON object
- Invalid catalog format from {url}
- Invalid catalog format from {url}: 'extensions' must be a JS
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/2a863bffe531aa9c.
Report an issue: GitHub.