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

  1. 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.
  2. Run the backfill when star churn is low (or immediately after announcing a freeze) so the list does not shift between page fetches.
  3. 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.
  4. 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

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


AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14). Data as JSON: /api/errors/e11522522aadd6f0. Report an issue: GitHub.