zed-industries/zed · error · RuntimeError

GraphQL error: {result['errors']}

Error message

GraphQL error: {result['errors']}

What it means

The triage queue board's github_graphql retries only HTTP statuses in RETRYABLE_STATUS_CODES (fixed sleep, 30s timeout); a 200 response carrying an "errors" array raises this RuntimeError with the raw errors. GraphQL application failures — the ones that come back with HTTP 200 — are therefore never retried.

Source

Thrown at script/github-triage-queue-board.py:56

def github_graphql(query, variables):
    for attempt in range(MAX_RETRIES + 1):
        response = requests.post(
            f"{GITHUB_API_URL}/graphql",
            headers=GITHUB_HEADERS,
            json={"query": query, "variables": variables},
            timeout=30,
        )
        if response.status_code in RETRYABLE_STATUS_CODES and attempt < MAX_RETRIES:
            print(
                f"GitHub API returned {response.status_code}, retrying in {RETRY_DELAY_SECONDS}s (attempt {attempt + 1}/{MAX_RETRIES})..."
            )
            time.sleep(RETRY_DELAY_SECONDS)
            continue
        response.raise_for_status()
        result = response.json()
        if "errors" in result:
            raise RuntimeError(f"GraphQL error: {result['errors']}")
        return result["data"]
    raise RuntimeError("github_graphql: retry loop exited without return")


def github_rest_get(path):
    for attempt in range(MAX_RETRIES + 1):
        response = requests.get(
            f"{GITHUB_API_URL}/{path}", headers=GITHUB_HEADERS, timeout=30
        )
        if response.status_code in RETRYABLE_STATUS_CODES and attempt < MAX_RETRIES:
            print(
                f"GitHub API returned {response.status_code}, retrying in {RETRY_DELAY_SECONDS}s (attempt {attempt + 1}/{MAX_RETRIES})..."
            )
            time.sleep(RETRY_DELAY_SECONDS)
            continue
        response.raise_for_status()
        return response.json()
    raise RuntimeError("github_rest_get: retry loop exited without return")

View on GitHub (pinned to bc538def45)

Solutions

  1. Read the errors array in the message to find the failing field/id/permission
  2. Refresh the token and confirm project scopes
  3. Re-run the query standalone with the same variables to reproduce
  4. Make body-level rate-limit errors retryable like the HTTP 429 branch

Example fix

// before
result = response.json()
if "errors" in result:
    raise RuntimeError(f"GraphQL error: {result['errors']}")

// after
result = response.json()
if "errors" in result:
    rate_limited = any("rate limit" in str(e.get("message", "")).lower() for e in result["errors"])
    if rate_limited and attempt < MAX_RETRIES:
        time.sleep(RETRY_DELAY_SECONDS)
        continue
    raise RuntimeError(f"GraphQL error: {result['errors']}")
Defensive patterns

Strategy: retry

Validate before calling

def graphql_request_is_well_formed(query: str, variables: dict) -> bool:
    return bool(query.strip()) and isinstance(variables, dict)

Type guard

def is_graphql_error_body(payload: dict) -> bool:
    return isinstance(payload, dict) and bool(payload.get("errors"))

Try / catch

try:
    data = github_graphql(query, variables)
except RuntimeError as exc:
    if "rate limit" in str(exc).lower():
        time.sleep(RETRY_DELAY_SECONDS)
        data = github_graphql(query, variables)
    else:
        raise

Prevention

When it happens

Trigger: Board queries return 200 + errors: stale project/item node ids, missing read:project on the token, deprecated fields in the query after schema changes, or rate-limit text embedded in the errors body.

Common situations: Expired CI token; project recreated so fetch_project_id/item ids go stale; GitHub schema evolution breaking a fragment; rate pressure from overlapping scheduled runs.

Related errors


AI-assisted analysis of zed-industries/zed@bc538def45 (2026-08-16). Data as JSON: /api/errors/03f156bffdde1bf4. Report an issue: GitHub.