666ghj/MiroFish · error · FetchError
GitHub API response had an unexpected shape
Error message
GitHub API response had an unexpected shape
What it means
After successful JSON parsing, the document must be a JSON object (Python dict). This error means the body parsed fine but was an array, string, number, boolean, or null — GitHub's /repos endpoint always returns an object, so a non-dict indicates the URL now serves a different resource or something rewrote the response.
Source
Thrown at scripts/fetch_star_count.py:118
if response.geturl() != API_URL:
raise FetchError("GitHub API redirect was refused")
status = response.getcode()
if status != 200:
raise _status_error(status)
payload = _read_response(response)
except FetchError:
raise
except (TimeoutError, OSError):
raise FetchError("GitHub API response could not be read") from None
except Exception:
raise FetchError("GitHub API response could not be processed") from None
try:
document = json.loads(payload)
except (UnicodeDecodeError, json.JSONDecodeError, ValueError):
raise FetchError("GitHub API returned malformed JSON") from None
if not isinstance(document, dict):
raise FetchError("GitHub API response had an unexpected shape")
count = document.get("stargazers_count")
if type(count) is not int or count < 0:
raise FetchError("GitHub API returned an invalid stargazers_count")
return count
def main(argv: list[str] | None = None) -> int:
arguments = sys.argv[1:] if argv is None else argv
if arguments:
print("error: this command accepts no arguments", file=sys.stderr)
return 2
try:
count = fetch_star_count(os.environ.get("GITHUB_TOKEN", ""))
except FetchError as exc:
print(f"error: {exc}", file=sys.stderr)
return 1View on GitHub (pinned to b5b53acc57)
Solutions
- Confirm API_URL is exactly https://api.github.com/repos/{owner}/{repo} with no suffix.
- Inspect the parsed type in a debug run: print(type(json.loads(payload))) and compare against a direct curl of the same URL.
- Fix test fixtures to return an object literal, not a list.
Defensive patterns
Strategy: type-guard
Type guard
import json
from typing import Any
def is_repo_document(document: Any) -> bool:
"""Narrow parsed JSON to the expected GitHub repository object."""
return (
isinstance(document, dict)
and isinstance(document.get("stargazers_count"), int)
) Try / catch
try:
count = fetch_star_count(token)
except FetchError as exc:
if exc.args[0] == "GitHub API response had an unexpected shape":
# parsed fine but not an object: URL drift or gateway rewrite
raise SystemExit("expected a JSON object from /repos; verify API_URL")
raise Prevention
- Pin API_URL construction to a single constant so it cannot drift toward list endpoints.
- Validate document shape before field access in your own integrations — dict-ness is the cheapest contract check.
- Test doubles should mirror the real object literal, including snake_case keys.
When it happens
Trigger: API_URL pointing at a collection endpoint that returns a JSON array; a proxy returning a JSON-encoded status object from a different service; test doubles returning json.dumps([1,2,3]).
Common situations: REPOSITORY constant edited to include a query string or path segment that changes the response shape; mock servers returning list payloads; gateway routing api.github.com to an internal service.
Related errors
- GitHub API returned an invalid stargazers_count
- GitHub API returned malformed JSON
- Ontology result must be an object
- LLM JSON output was truncated at the token limit
- LLM JSON generation stopped unexpectedly ({finish_reason})
AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14).
Data as JSON: /api/errors/ba2ae3096c4e373b.
Report an issue: GitHub.