zed-industries/zed · error · RuntimeError

GraphQL error: {result['errors']}

Error message

GraphQL error: {result['errors']}

What it means

The guild board's github_graphql helper retries only HTTP statuses in RETRYABLE_STATUS_CODES (with a fixed sleep and a 30s timeout per request); a 200 response whose body contains an "errors" array raises this RuntimeError with the raw errors. Application-level GraphQL failures therefore bypass the retry loop entirely.

Source

Thrown at script/github-guild-board.py:112

    "A tell-tale ping beneath the board.",
]


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:
            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_request(method, path, body=None):
    url = f"{GITHUB_API_URL}/{path}"
    for attempt in range(MAX_RETRIES + 1):
        response = requests.request(
            method, url, headers=GITHUB_HEADERS, json=body, timeout=30
        )
        if response.status_code in RETRYABLE_STATUS_CODES and attempt < MAX_RETRIES:
            time.sleep(RETRY_DELAY_SECONDS)
            continue
        response.raise_for_status()
        if response.status_code == 204 or not response.content:
            return None
        return response.json()
    raise RuntimeError("github_rest_request: retry loop exited without return")

View on GitHub (pinned to bc538def45)

Solutions

  1. Inspect result['errors'] in the message: type and message identify the failing field, id, or permission
  2. Verify token validity and scopes (read:project, plus write for the mutations)
  3. Refresh any hardcoded node ids or project numbers whose objects were recreated
  4. Treat body-level rate-limit errors as retryable in the loop, 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:
    transient = any("rate limit" in str(e.get("message", "")).lower() for e in result["errors"])
    if transient 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 or the ProjectV2 mutations return 200 + errors: stale project/item node ids, missing project scopes on the token, deprecated GraphQL fields after schema churn, or rate-limit messages placed in the body.

Common situations: Token rotated without project scopes; projects/issues recreated invalidating node ids; GitHub schema changes deprecating a field used in the query's fragments; sustained CI load triggering secondary rate limits.

Related errors


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