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

  1. Log the offending edge value to confirm whether it is null or a scalar
  2. If null edges from deleted accounts are observed, filter them before parsing (GitHub documents that edges may contain null)
  3. 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

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


AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14). Data as JSON: /api/errors/6f095bb8c3b19372. Report an issue: GitHub.