666ghj/MiroFish · warning · StarHistoryError
GitHub GraphQL returned an invalid edge
Error message
GitHub GraphQL returned an invalid edge
What it means
While iterating the stargazers edges list, one element was not a JSON object. Each edge must be a dict so .get('cursor') and .get('starredAt') work; anything else (string, number, null, nested list) raises StarHistoryError('GitHub GraphQL returned an invalid edge').
Source
Thrown at scripts/star_history.py:274
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:
if not isinstance(raw_edge, dict):
raise StarHistoryError("GitHub GraphQL returned an invalid edge")
cursor = raw_edge.get("cursor")
starred_at = raw_edge.get("starredAt")
if not isinstance(cursor, str) or not cursor:
raise StarHistoryError("GitHub GraphQL returned an invalid edge cursor")
if not isinstance(starred_at, str):
raise StarHistoryError("GitHub GraphQL returned an invalid star timestamp")
edges.append(StargazerEdge(cursor, _parse_github_timestamp(starred_at)))
has_next_page = page_info.get("hasNextPage")
end_cursor = page_info.get("endCursor")
if type(has_next_page) is not bool:
raise StarHistoryError("GitHub GraphQL returned invalid page information")
if end_cursor is not None and not isinstance(end_cursor, str):
raise StarHistoryError("GitHub GraphQL returned an invalid page cursor")
if has_next_page and not end_cursor:
raise StarHistoryError("GitHub GraphQL omitted the next page cursor")
return StargazerPage(View on GitHub (pinned to b5b53acc57)
Solutions
- Log the offending edge value to confirm whether it is null or a scalar
- If null edges from deleted accounts are observed, filter them before parsing (GitHub documents that edges may contain null)
- Correct test fixtures so edges entries are objects with cursor/starredAt keys
Example fix
// before
for raw_edge in raw_edges:
if not isinstance(raw_edge, dict):
raise StarHistoryError("GitHub GraphQL returned an invalid edge")
// after
for raw_edge in raw_edges:
if raw_edge is None:
continue # skip edges for deleted accounts
if not isinstance(raw_edge, dict):
raise StarHistoryError("GitHub GraphQL returned an invalid edge") Defensive patterns
Strategy: validation
Validate before calling
clean_edges = [e for e in raw_edges if isinstance(e, dict)]
if len(clean_edges) != len(raw_edges):
logger.warning("dropped %d non-object edges", len(raw_edges) - len(clean_edges)) Type guard
def is_edge_object(edge: object) -> TypeGuard[dict]:
return isinstance(edge, dict) Try / catch
try:
page = _parse_graphql_page(payload)
except StarHistoryError as exc:
if "invalid edge" in str(exc):
logger.warning("skipping malformed edge batch: %s", exc)
# safe only if you can re-fetch the page; do not silently lose stars
raise
raise Prevention
- Filter null edges (deleted accounts) before parsing — GitHub documents edges may contain null
- Build fixtures from recorded real responses rather than hand-typing edge structures
When it happens
Trigger: An edges array containing null entries (deleted/hidden user accounts can serialize as null nodes), or an edges array of scalars from a hand-edited query or a test fixture.
Common situations: Real GitHub responses where an edge's node is null (user deleted their account between starring and pagination); test fixtures built by hand with edges: ['cursor1', 'cursor2'].
Related errors
- GitHub GraphQL edges were not a list
- GitHub GraphQL returned an invalid star timestamp
- GitHub GraphQL returned an invalid edge cursor
- GitHub GraphQL returned invalid page information
- GitHub GraphQL returned an invalid page cursor
AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14).
Data as JSON: /api/errors/6f095bb8c3b19372.
Report an issue: GitHub.