666ghj/MiroFish · error · StarHistoryError
GitHub GraphQL omitted the next page cursor
Error message
GitHub GraphQL omitted the next page cursor
What it means
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.
Source
Thrown at scripts/star_history.py:290
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(
total_count=total_count,
edges=tuple(edges),
has_next_page=has_next_page,
end_cursor=end_cursor,
rate_remaining=rate_remaining,
)
def _strict_non_negative_int(value: Any, label: str) -> int:
if type(value) is not int or value < 0:
raise StarHistoryError(f"{label} must be a non-negative integer")
return value
def _parse_github_timestamp(value: str) -> datetime:
try:View on GitHub (pinned to b5b53acc57)
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
Example fix
// before (fixture)
"pageInfo": {"hasNextPage": true, "endCursor": null}
// after (fixture)
"pageInfo": {"hasNextPage": true, "endCursor": "Y3Vyc29yOnYyODI5NQ=="} Defensive patterns
Strategy: validation
Validate before calling
if page_info.get("hasNextPage") is True and not page_info.get("endCursor"):
raise StarHistoryError("hasNextPage without endCursor — pagination would repeat the page") Type guard
def next_page_navigable(page_info: dict) -> bool:
return page_info.get("hasNextPage") is False or bool(page_info.get("endCursor")) Try / catch
try:
page = _parse_graphql_page(payload)
except StarHistoryError as exc:
if "omitted the next page cursor" in str(exc):
raise StarHistoryError("stopping pagination: server signalled more pages but gave no cursor") from exc
raise Prevention
- Stub servers must pair hasNextPage:true with a non-empty endCursor
- Fail fast on contradictory pagination state — looping the same page silently duplicates data
When it happens
Trigger: 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.
Common situations: Test fixtures exercising the 'more pages' path without copying a real cursor; a mock that defaults endCursor to null.
Related errors
- GitHub GraphQL returned an invalid edge cursor
- GitHub GraphQL returned invalid page information
- GitHub GraphQL returned an invalid page cursor
- GitHub returned an invalid pagination cursor
- GitHub GraphQL edges were not a list
AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14).
Data as JSON: /api/errors/ce1c20fa475461a7.
Report an issue: GitHub.