{"record":{"id":"ce1c20fa475461a7","repo":"666ghj/MiroFish","slug":"github-graphql-omitted-the-next-page-cursor","errorCode":null,"errorMessage":"GitHub GraphQL omitted the next page cursor","messagePattern":"GitHub GraphQL omitted the next page cursor","errorType":"exception","errorClass":"StarHistoryError","httpStatus":null,"severity":"error","filePath":"scripts/star_history.py","lineNumber":290,"sourceCode":"        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,\n            rate_remaining=rate_remaining,\n        )\n\n\ndef _strict_non_negative_int(value: Any, label: str) -> int:\n    if type(value) is not int or value < 0:\n        raise StarHistoryError(f\"{label} must be a non-negative integer\")\n    return value\n\n\ndef _parse_github_timestamp(value: str) -> datetime:\n    try:","sourceCodeStart":272,"sourceCodeEnd":308,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/scripts/star_history.py#L272-L308","documentation":"pageInfo.hasNextPage was true but endCursor was null or an empty string. Without a cursor there is no way to request the next page, so continuing would silently loop the first page forever; the parser raises StarHistoryError('GitHub GraphQL omitted the next page cursor') to fail fast instead.","triggerScenarios":"Response with hasNextPage: true and endCursor: null (or \"\") — usually a fixture that set hasNextPage without providing a cursor, or a stubbed server; on the real API this combination is effectively impossible.","commonSituations":"Test fixtures exercising the 'more pages' path without copying a real cursor; a mock that defaults endCursor to null.","solutions":["Give the fixture a non-empty endCursor whenever hasNextPage is true","Or set hasNextPage: false when the fixture has no cursor","Verify no code path strips endCursor from the response before parsing"],"exampleFix":"// before (fixture)\n\"pageInfo\": {\"hasNextPage\": true, \"endCursor\": null}\n\n// after (fixture)\n\"pageInfo\": {\"hasNextPage\": true, \"endCursor\": \"Y3Vyc29yOnYyODI5NQ==\"}","handlingStrategy":"validation","validationCode":"if page_info.get(\"hasNextPage\") is True and not page_info.get(\"endCursor\"):\n    raise StarHistoryError(\"hasNextPage without endCursor — pagination would repeat the page\")","typeGuard":"def next_page_navigable(page_info: dict) -> bool:\n    return page_info.get(\"hasNextPage\") is False or bool(page_info.get(\"endCursor\"))","tryCatchPattern":"try:\n    page = _parse_graphql_page(payload)\nexcept StarHistoryError as exc:\n    if \"omitted the next page cursor\" in str(exc):\n        raise StarHistoryError(\"stopping pagination: server signalled more pages but gave no cursor\") from exc\n    raise","preventionTips":["Stub servers must pair hasNextPage:true with a non-empty endCursor","Fail fast on contradictory pagination state — looping the same page silently duplicates data"],"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"}