{"record":{"id":"d02a9e12e2a5d0eb","repo":"666ghj/MiroFish","slug":"github-graphql-returned-an-invalid-page-cursor","errorCode":null,"errorMessage":"GitHub GraphQL returned an invalid page cursor","messagePattern":"GitHub GraphQL returned an invalid page cursor","errorType":"exception","errorClass":"StarHistoryError","httpStatus":null,"severity":"error","filePath":"scripts/star_history.py","lineNumber":288,"sourceCode":"\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,\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","sourceCodeStart":270,"sourceCodeEnd":306,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/scripts/star_history.py#L270-L306","documentation":"pageInfo.endCursor was present but not a string (None is allowed and handled separately — the failure is for numbers, dicts, lists, bools). endCursor is the opaque pagination token for the next request, so the parser raises StarHistoryError('GitHub GraphQL returned an invalid page cursor') when it cannot be used as one.","triggerScenarios":"endCursor: 12345, an object, or a list in the response — typically from a fixture or a modified/stubbed GraphQL endpoint, since real GitHub always returns a string or null.","commonSituations":"Hand-built fixtures where endCursor was an integer cursor index; a mock server returning {'endCursor': {'value': 'x'}}.","solutions":["Fix the fixture/mock to return endCursor as a string or null","Log the raw end_cursor value to identify its actual type","Re-run against the real GitHub endpoint to confirm it is a stub-only issue"],"exampleFix":"// before (fixture)\n\"endCursor\": 42\n\n// after (fixture)\n\"endCursor\": \"42\"","handlingStrategy":"validation","validationCode":"end_cursor = page_info.get(\"endCursor\", \"missing\")\nif end_cursor != \"missing\" and end_cursor is not None and not isinstance(end_cursor, str):\n    raise StarHistoryError(\"endCursor is not a string\")","typeGuard":"def end_cursor_ok(value: object) -> bool:\n    return value is None or isinstance(value, str)","tryCatchPattern":"try:\n    page = _parse_graphql_page(payload)\nexcept StarHistoryError as exc:\n    if \"invalid page cursor\" in str(exc):\n        raise StarHistoryError(\"pagination token unusable; refusing to continue\") from exc\n    raise","preventionTips":["Keep endCursor as a string in all fixtures and stubs; null means 'no more pages'","Never coerce cursors between types — they are opaque tokens"],"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"}