666ghj/MiroFish · error · StarHistoryError
stargazer list changed or was incomplete during backfill
Error message
stargazer list changed or was incomplete during backfill
What it means
Raised at scripts/star_history.py:609 after the backfill completes. The script compares `len(seen_edge_cursors)` against the `totalCount` reported by the very first page (`initial_total`); a mismatch means the pages traversed did not cover exactly the stargazer set GitHub advertised, so the per-day star counts in `daily_increments` would be wrong (under- or over-counted). The state file is therefore not written.
Source
Thrown at scripts/star_history.py:609
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")
if len(seen_edge_cursors) != initial_total:
raise StarHistoryError("stargazer list changed or was incomplete during backfill")
running = 0
daily: list[dict[str, Any]] = []
for point_day in sorted(daily_increments):
running += daily_increments[point_day]
daily.append({"date": point_day.isoformat(), "stars": running})
state: dict[str, Any] = {
"schema_version": 1,
"repository": REPOSITORY,
"timezone": "UTC",
"ongoing_interval_days": INTERVAL_DAYS,
"reconstruction": {
"method": "current_stargazers_starred_at",
"generated_at": _format_state_timestamp(normalized_now),
"daily": daily,
},
"snapshots": [],View on GitHub (pinned to b5b53acc57)
Solutions
- Re-run the backfill at a quieter time — churn-induced mismatch is by far the most common cause and a retry usually converges.
- If it persists on a busy repo, retry a few times; small mismatches (±1–2) resolve as churn settles, and the check guarantees you never persist a wrong history.
- In test doubles, make `total_count` exactly equal the total number of distinct edges the fake will return across all pages.
- For very large repos, verify the star count with `gh api repos/{owner}/{repo} --jq .stargazers_count` before and after the run to confirm whether real churn explains the delta.
Example fix
# test double: total_count must match the sum of edges over all pages
TOTAL = 500
# before: StargazerPage(total_count=999, ...)
# after:
return StargazerPage(
total_count=TOTAL,
edges=all_edges[page_index * 100:(page_index + 1) * 100],
has_next_page=(page_index + 1) * 100 < TOTAL,
end_cursor=f"page-{page_index}" if (page_index + 1) * 100 < TOTAL else None,
rate_remaining=5000,
) Defensive patterns
Strategy: retry
Try / catch
for attempt in range(3):
try:
state = run_backfill(gateway, clock)
break
except StarHistoryError as exc:
if "changed or was incomplete" not in str(exc) or attempt == 2:
raise
logger.info("stargazer churn during backfill (attempt %d); retrying", attempt + 1) Prevention
- Schedule the backfill during low star-churn windows (check GitHub traffic insights first).
- Do not extend the pagination loop with sleeps — longer runs increase churn exposure and burn rate limit.
- If mismatches of exactly ±1 persist, compare `gh api repos/... --jq .stargazers_count` before and after the run to confirm real churn rather than a parsing bug.
- Remember the guard protects you: a mismatched count would silently corrupt every daily total downstream.
When it happens
Trigger: Stars added or removed between the first page fetch (which fixes `initial_total`) and the last page: every new star means an extra edge collected (len > total), every un-star means one missing (len < total). Also fires when GitHub's `totalCount` is eventually consistent with the edge stream on huge repositories.
Common situations: Backfilling an actively starred repository (e.g. trending on GitHub) where churn during a multi-minute pagination run is guaranteed; running the one-time maintainer backfill right after a publicity event; test gateways whose `total_count` field disagrees with the number of edges they emit across all pages.
Related errors
- GitHub GraphQL repeated an edge cursor
- GitHub GraphQL repeated a page cursor
- GitHub GraphQL exceeded the page safety limit
- Persisted Zep batch does not match the current graph input
- Zep batch {batch_id} acknowledged {len(item_details or [])}
AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14).
Data as JSON: /api/errors/2340744739965b50.
Report an issue: GitHub.