666ghj/MiroFish · error · StarHistoryError
GitHub GraphQL response had an unexpected shape
Error message
GitHub GraphQL response had an unexpected shape
What it means
Shape validation of the GraphQL data payload: either the nested field extraction (payload['data']['repository']['stargazers']['edges'/'pageInfo'], data['rateLimit']) raised KeyError/TypeError because a field is missing or a parent is null, or the subsequent all(isinstance(v, dict)) check found one of data/repository/stargazers/rate_limit/page_info is not a dict. A common legitimate cause is repository: null when owner/name point to a nonexistent or inaccessible repo, which makes the subscript chain fail.
Source
Thrown at scripts/star_history.py:254
raise StarHistoryError(
f"GitHub GraphQL request failed (exit {completed.returncode})"
)
try:
payload = json.loads(completed.stdout)
except json.JSONDecodeError as exc:
raise StarHistoryError("GitHub GraphQL returned malformed JSON") from exc
if not isinstance(payload, dict) or payload.get("errors"):
raise StarHistoryError("GitHub GraphQL rejected the stargazer request")
try:
data = payload["data"]
repository = data["repository"]
stargazers = repository["stargazers"]
rate_limit = data["rateLimit"]
raw_edges = stargazers["edges"]
page_info = stargazers["pageInfo"]
except (KeyError, TypeError) as exc:
raise StarHistoryError("GitHub GraphQL response had an unexpected shape") from exc
if not all(
isinstance(value, dict)
for value in (data, repository, stargazers, rate_limit, page_info)
):
raise StarHistoryError("GitHub GraphQL response had an unexpected shape")
total_count = _strict_non_negative_int(
stargazers.get("totalCount"), "GraphQL totalCount"
)
rate_remaining = _strict_non_negative_int(
rate_limit.get("remaining"), "GraphQL rate remaining"
)
if not isinstance(raw_edges, list):
raise StarHistoryError("GitHub GraphQL edges were not a list")
edges: list[StargazerEdge] = []
for raw_edge in raw_edges:View on GitHub (pinned to b5b53acc57)
Solutions
- Check the repo resolves: curl https://api.github.com/repos/666ghj/MiroFish — 404 or null means renamed/private; update REPOSITORY in scripts/star_history.py (and the fetch_star_count.py copy) accordingly.
- If data['repository'] is None specifically, it is the nonexistent/inaccessible case above; any other missing key means the pinned GRAPHQL_QUERY no longer matches the schema — diff it against GitHub's current stargazers type.
- For tests, build fixtures from a captured real response rather than hand-writing the nested structure.
Example fix
# before: null repository surfaces as opaque shape error
data = payload["data"]
repository = data["repository"]
stargazers = repository["stargazers"] # TypeError: 'NoneType' ...
# after: name the real cause before subscripting
repository = (payload.get("data") or {}).get("repository")
if repository is None:
raise StarHistoryError(
f"GitHub GraphQL returned no repository for "
f"{REPOSITORY_OWNER}/{REPOSITORY_NAME} (renamed, private, or gone)"
) Defensive patterns
Strategy: type-guard
Type guard
from typing import Any
def is_stargazer_page_payload(payload: Any) -> bool:
"""Full expected shape: data.repository.stargazers{edges,pageInfo} + rateLimit."""
try:
data = payload["data"]
stargazers = data["repository"]["stargazers"]
return all(
isinstance(node, dict)
for node in (data, data["repository"], stargazers,
data["rateLimit"], stargazers["pageInfo"])
) and isinstance(stargazers.get("edges"), list)
except (KeyError, TypeError):
return False Try / catch
try:
page = gateway.fetch_stargazer_page(after)
except StarHistoryError as exc:
if "unexpected shape" in str(exc):
repository = (payload.get("data") or {}).get("repository")
if repository is None:
raise SystemExit(
"repo 666ghj/MiroFish not visible: renamed, private, "
"or token lost access — update REPOSITORY constants"
)
raise SystemExit("GraphQL schema drift; update GRAPHQL_QUERY")
raise Prevention
- Check repository null first — it is the overwhelmingly common cause and deserves its own message.
- Freeze a real GraphQL response as the test fixture instead of hand-building nested dicts.
- After any repo rename/visibility change, update REPOSITORY in star_history.py and fetch_star_count.py in the same commit.
When it happens
Trigger: REPOSITORY_OWNER/REPOSITORY_NAME constants pointing at a renamed/deleted/private repo (repository null); GraphQL partial data where rateLimit is omitted under errors; pageInfo absent on the last page's shape change; test fixtures missing edges or pageInfo keys.
Common situations: Repository 666ghj/MiroFish renamed or made private after the constants were pinned; token losing access mid-backfill; GitHub returning partial data during an incident; hand-written test payloads missing nested keys.
Related errors
- GitHub GraphQL rejected the stargazer request
- GitHub returned an invalid pagination cursor
- GitHub GraphQL edges were not a list
- GitHub GraphQL returned an invalid edge
- GitHub GraphQL returned an invalid edge cursor
AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14).
Data as JSON: /api/errors/837c89fb4458722b.
Report an issue: GitHub.