666ghj/MiroFish · error · StarHistoryError

GitHub GraphQL rejected the stargazer request

Error message

GitHub GraphQL rejected the stargazer request

What it means

GraphQL-level rejection: the parsed JSON body is not an object, or it contains a top-level "errors" array. With gh exit 0 this still happens — GraphQL returns 200 with an errors array for query problems, type errors, bad cursor values (BAD_CURSOR / pagination cost), or insufficient scopes.

Source

Thrown at scripts/star_history.py:245

            f"name={REPOSITORY_NAME}",
        ]
        if after is not None:
            if not after or "\n" in after or "\r" in after:
                raise StarHistoryError("GitHub returned an invalid pagination cursor")
            arguments.extend(("-f", f"after={after}"))

        completed = self._runner.run(arguments)
        if completed.returncode != 0:
            raise StarHistoryError(
                f"GitHub GraphQL request failed (exit {completed.returncode})"
            )
        try:
            payload = json.loads(completed.stdout)
        except json.JSONDecodeError as exc:
            raise StarHistoryError("GitHub GraphQL returned malformed JSON") from exc

        if not isinstance(payload, dict) or payload.get("errors"):
            raise StarHistoryError("GitHub GraphQL rejected the stargazer request")
        try:
            data = payload["data"]
            repository = data["repository"]
            stargazers = repository["stargazers"]
            rate_limit = data["rateLimit"]
            raw_edges = stargazers["edges"]
            page_info = stargazers["pageInfo"]
        except (KeyError, TypeError) as exc:
            raise StarHistoryError("GitHub GraphQL response had an unexpected shape") from exc

        if not all(
            isinstance(value, dict)
            for value in (data, repository, stargazers, rate_limit, page_info)
        ):
            raise StarHistoryError("GitHub GraphQL response had an unexpected shape")

        total_count = _strict_non_negative_int(
            stargazers.get("totalCount"), "GraphQL totalCount"

View on GitHub (pinned to b5b53acc57)

Solutions

  1. Print payload['errors'] (it contains messages like 'wasn't a valid cursor') to identify the exact GraphQL complaint.
  2. If the cursor is stale/corrupt, drop back to the last known-good cursor in history.json and re-fetch that page.
  3. Verify the token's scopes with gh auth status; public stargazer reads need public access.
  4. If the schema changed, update GRAPHQL_QUERY in scripts/star_history.py to the current stargazers/rateLimit shape and re-run.

Example fix

# before: generic rejection message
if not isinstance(payload, dict) or payload.get("errors"):
    raise StarHistoryError("GitHub GraphQL rejected the stargazer request")

# after: include the GraphQL error messages (bounded)
if not isinstance(payload, dict) or payload.get("errors"):
    msgs = [
        e.get("message", "")[:120]
        for e in (payload.get("errors") or [])[:3]
        if isinstance(e, dict)
    ]
    raise StarHistoryError(
        "GitHub GraphQL rejected the stargazer request"
        + (f": {'; '.join(msgs)}" if msgs else "")
    )
Defensive patterns

Strategy: try-catch

Type guard

from typing import Any

def is_graphql_success(payload: Any) -> bool:
    """200 + no top-level errors + object body before field access."""
    return isinstance(payload, dict) and not payload.get("errors")

Try / catch

try:
    page = gateway.fetch_stargazer_page(after)
except StarHistoryError as exc:
    if "rejected the stargazer request" in str(exc):
        # GraphQL errors array holds the reason (e.g. invalid cursor);
        # fetch the raw payload once via `gh api graphql` to read it
        raise SystemExit("GraphQL-level failure; re-run gh manually and read "
                         "the errors[] messages")
    raise

Prevention

When it happens

Trigger: Passing an invalid or stale after cursor (GitHub returns '... is not a valid cursor'); GraphQL variables owner/name of a wrong type; missing rateLimit or stargazers field in a pinned query against a changed schema; token lacking public repo read; query cost exceeding GraphQL rate limits.

Common situations: Resuming backfill with a cursor from a different GraphQL endpoint or an old schema; gh token scopes trimmed; GitHub GraphQL API schema evolution breaking the pinned GRAPHQL_QUERY.

Related errors


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