{"record":{"id":"9684808b97f7280c","repo":"666ghj/MiroFish","slug":"github-graphql-returned-an-invalid-edge-cursor","errorCode":null,"errorMessage":"GitHub GraphQL returned an invalid edge cursor","messagePattern":"GitHub GraphQL returned an invalid edge cursor","errorType":"exception","errorClass":"StarHistoryError","httpStatus":null,"severity":"error","filePath":"scripts/star_history.py","lineNumber":278,"sourceCode":"            raise StarHistoryError(\"GitHub GraphQL response had an unexpected shape\")\n\n        total_count = _strict_non_negative_int(\n            stargazers.get(\"totalCount\"), \"GraphQL totalCount\"\n        )\n        rate_remaining = _strict_non_negative_int(\n            rate_limit.get(\"remaining\"), \"GraphQL rate remaining\"\n        )\n        if not isinstance(raw_edges, list):\n            raise StarHistoryError(\"GitHub GraphQL edges were not a list\")\n\n        edges: list[StargazerEdge] = []\n        for raw_edge in raw_edges:\n            if not isinstance(raw_edge, dict):\n                raise StarHistoryError(\"GitHub GraphQL returned an invalid edge\")\n            cursor = raw_edge.get(\"cursor\")\n            starred_at = raw_edge.get(\"starredAt\")\n            if not isinstance(cursor, str) or not cursor:\n                raise StarHistoryError(\"GitHub GraphQL returned an invalid edge cursor\")\n            if not isinstance(starred_at, str):\n                raise StarHistoryError(\"GitHub GraphQL returned an invalid star timestamp\")\n            edges.append(StargazerEdge(cursor, _parse_github_timestamp(starred_at)))\n\n        has_next_page = page_info.get(\"hasNextPage\")\n        end_cursor = page_info.get(\"endCursor\")\n        if type(has_next_page) is not bool:\n            raise StarHistoryError(\"GitHub GraphQL returned invalid page information\")\n        if end_cursor is not None and not isinstance(end_cursor, str):\n            raise StarHistoryError(\"GitHub GraphQL returned an invalid page cursor\")\n        if has_next_page and not end_cursor:\n            raise StarHistoryError(\"GitHub GraphQL omitted the next page cursor\")\n\n        return StargazerPage(\n            total_count=total_count,\n            edges=tuple(edges),\n            has_next_page=has_next_page,\n            end_cursor=end_cursor,","sourceCodeStart":260,"sourceCodeEnd":296,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/scripts/star_history.py#L260-L296","documentation":"An edge object lacked a usable cursor: cursor was not a str, or was an empty string. Cursors are mandatory for cursor-based pagination of stargazers (they are passed as the $after variable for the next page), so the parser raises StarHistoryError('GitHub GraphQL returned an invalid edge cursor') rather than attempting to continue from a missing position.","triggerScenarios":"An edge with cursor: null (cursor field not selected in the query, or GitHub omitted it), cursor: 123, or cursor: \"\". Also happens if the query was edited to drop the cursor selection while the parser still expects it.","commonSituations":"Hand-modified GraphQL query that removed `cursor` from the edges selection; test fixtures copying edges without cursors; GitHub API behavior change.","solutions":["Restore `cursor` in the edges selection of the GraphQL query if it was removed","Fix test fixtures so every edge has a non-empty string cursor","Log the failing edge to distinguish null vs empty string — null means the field was not selected, empty means malformed data"],"exampleFix":"// before (query fragment)\nedges { node { ... } }\n\n// after (query fragment)\nedges { cursor starredAt }","handlingStrategy":"validation","validationCode":"for edge in raw_edges:\n    if not isinstance(edge, dict) or not isinstance(edge.get(\"cursor\"), str) or not edge[\"cursor\"]:\n        raise StarHistoryError(\"edge without usable cursor; cannot paginate safely\")\n        break","typeGuard":"def has_usable_cursor(edge: object) -> TypeGuard[dict]:\n    return isinstance(edge, dict) and isinstance(edge.get(\"cursor\"), str) and bool(edge[\"cursor\"])","tryCatchPattern":"try:\n    page = _parse_graphql_page(payload)\nexcept StarHistoryError as exc:\n    if \"invalid edge cursor\" in str(exc):\n        raise StarHistoryError(\"pagination aborted: GitHub omitted a cursor — restart collection from last good cursor\") from exc\n    raise","preventionTips":["Always select `cursor` in the edges block of the GraphQL query","Persist the last good cursor so an aborted pagination run can resume instead of restarting"],"tags":["github-api","graphql","pagination","validation","star-history"],"backgroundTag":null,"analyzedSha":"b5b53acc57189a4a42e44a23e149dc655c98fe82","analyzedAt":"2026-08-14T22:29:33.146Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}