666ghj/MiroFish · error · StarHistoryError
GitHub GraphQL repeated an edge cursor
Error message
GitHub GraphQL repeated an edge cursor
What it means
Raised while paginating the GitHub GraphQL stargazer list during the one-time backfill (scripts/star_history.py:589). Each stargazer edge carries a unique pagination cursor; this script keeps a `seen_edge_cursors` set and refuses to continue if GitHub hands back an edge whose cursor was already delivered on an earlier page. It is an integrity guard: silently accepting a repeat would double-count stars in `daily_increments` and corrupt the reconstructed history.
Source
Thrown at scripts/star_history.py:589
after: str | None = None
seen_page_cursors: set[str] = set()
seen_edge_cursors: set[str] = set()
daily_increments: Counter[date] = Counter()
initial_total: int | None = None
page_number = 0
while True:
page = github.fetch_stargazer_page(after)
page_number += 1
if initial_total is None:
initial_total = page.total_count
pages_required = math.ceil(initial_total / PAGE_SIZE)
remaining_requests = max(0, pages_required - 1)
if page.rate_remaining < remaining_requests + RATE_LIMIT_RESERVE:
raise StarHistoryError("insufficient GitHub GraphQL rate limit for backfill")
for edge in page.edges:
if edge.cursor in seen_edge_cursors:
raise StarHistoryError("GitHub GraphQL repeated an edge cursor")
seen_edge_cursors.add(edge.cursor)
if edge.starred_at.date() < normalized_now.date():
daily_increments[edge.starred_at.date()] += 1
if page.end_cursor is not None:
if page.end_cursor in seen_page_cursors:
raise StarHistoryError("GitHub GraphQL repeated a page cursor")
seen_page_cursors.add(page.end_cursor)
if not page.has_next_page:
break
if page.end_cursor is None:
raise StarHistoryError("GitHub GraphQL omitted the next page cursor")
after = page.end_cursor
if page_number > 10_000:
raise StarHistoryError("GitHub GraphQL exceeded the page safety limit")
if initial_total is None:
raise StarHistoryError("GitHub GraphQL returned no pages")View on GitHub (pinned to b5b53acc57)
Solutions
- Simply re-run the backfill from scratch: it is a fresh-start operation, and a transient pagination overlap usually disappears on a second run when star churn has settled.
- Run the backfill when star churn is low (or immediately after announcing a freeze) so the list does not shift between page fetches.
- If the error reproduces deterministically, capture the raw `gh api graphql` response for the failing page and inspect whether GitHub is returning duplicate edges/cursors; report it as an upstream pagination inconsistency.
- If using a custom GitHubGateway implementation in tests, fix the fake gateway so each page yields distinct edge cursors (e.g. generate cursors from a monotonic counter).
Example fix
// test fake gateway that repeated cursors across pages
class FakeGateway:
def fetch_stargazer_page(self, after):
# before: every page reused cursor "edge-0"
# after: cursors derived from the edge index are unique per page
edges = tuple(
StargazerEdge(cursor=f"edge-{page_index * 100 + i}",
starred_at=START + timedelta(days=page_index, minutes=i))
for i in range(100)
)
... Defensive patterns
Strategy: retry
Try / catch
try:
state = run_backfill(gateway, clock)
except StarHistoryError as exc:
if "repeated an edge cursor" in str(exc):
logger.warning("backfill saw duplicate edge cursor; retrying once: %s", exc)
state = run_backfill(gateway, clock) # fresh seen-sets each run
else:
raise Prevention
- Run the one-time backfill when the repository is not actively gaining/losing stars (avoid trending periods).
- Treat backfill as idempotent-from-scratch: always restart the whole pagination, never resume mid-way from a saved cursor.
- In test gateways, derive edge cursors from a global index so pages never overlap.
- Log page_number alongside the error so a repeat at the same page points to upstream pagination rather than churn.
When it happens
Trigger: Calling the backfill that loops `github.fetch_stargazer_page(after)` (100 edges per page, PAGE_SIZE=100) on a repository whose stargazer list is concurrently changing, or when GitHub's GraphQL pagination returns overlapping pages. Concretely: page N returns edges [c1..c100], a later page returns an edge whose cursor is already in `seen_edge_cursors` -> raise before the `daily_increments[edge.starred_at.date()] += 1` line can double count.
Common situations: Backfill run on an actively starred repository (stars arrive between page fetches), GitHub eventual-consistency quirks on large `stargazers(first:100, after:...)` traversals, or a mock/test gateway that replays the same page twice. Typical for maintainers running the documented one-time authorized backfill via `gh api graphql`.
Related errors
- GitHub GraphQL repeated a page cursor
- GitHub GraphQL returned an invalid edge cursor
- GitHub GraphQL returned invalid page information
- GitHub GraphQL returned an invalid page cursor
- GitHub GraphQL omitted the next page cursor
AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14).
Data as JSON: /api/errors/e11522522aadd6f0.
Report an issue: GitHub.