{"record":{"id":"81aa94b8f11f5deb","repo":"zed-industries/zed","slug":"graphql-error-result-errors-81aa94","errorCode":null,"errorMessage":"GraphQL error: {result['errors']}","messagePattern":"GraphQL error: (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"script/github-guild-board.py","lineNumber":112,"sourceCode":"    \"A tell-tale ping beneath the board.\",\n]\n\n\ndef github_graphql(query, variables):\n    for attempt in range(MAX_RETRIES + 1):\n        response = requests.post(\n            f\"{GITHUB_API_URL}/graphql\",\n            headers=GITHUB_HEADERS,\n            json={\"query\": query, \"variables\": variables},\n            timeout=30,\n        )\n        if response.status_code in RETRYABLE_STATUS_CODES and attempt < MAX_RETRIES:\n            time.sleep(RETRY_DELAY_SECONDS)\n            continue\n        response.raise_for_status()\n        result = response.json()\n        if \"errors\" in result:\n            raise RuntimeError(f\"GraphQL error: {result['errors']}\")\n        return result[\"data\"]\n    raise RuntimeError(\"github_graphql: retry loop exited without return\")\n\n\ndef github_rest_request(method, path, body=None):\n    url = f\"{GITHUB_API_URL}/{path}\"\n    for attempt in range(MAX_RETRIES + 1):\n        response = requests.request(\n            method, url, headers=GITHUB_HEADERS, json=body, timeout=30\n        )\n        if response.status_code in RETRYABLE_STATUS_CODES and attempt < MAX_RETRIES:\n            time.sleep(RETRY_DELAY_SECONDS)\n            continue\n        response.raise_for_status()\n        if response.status_code == 204 or not response.content:\n            return None\n        return response.json()\n    raise RuntimeError(\"github_rest_request: retry loop exited without return\")","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/zed-industries/zed/blob/bc538def4545534201bbfcac4e95ac34ea6501b6/script/github-guild-board.py#L94-L130","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect result['errors'] in the message: type and message identify the failing field, id, or permission","Verify token validity and scopes (read:project, plus write for the mutations)","Refresh any hardcoded node ids or project numbers whose objects were recreated","Treat body-level rate-limit errors as retryable in the loop, like the HTTP 429 branch"],"exampleFix":"// before\nresult = response.json()\nif \"errors\" in result:\n    raise RuntimeError(f\"GraphQL error: {result['errors']}\")\n\n// after\nresult = response.json()\nif \"errors\" in result:\n    transient = any(\"rate limit\" in str(e.get(\"message\", \"\")).lower() for e in result[\"errors\"])\n    if transient and attempt < MAX_RETRIES:\n        time.sleep(RETRY_DELAY_SECONDS)\n        continue\n    raise RuntimeError(f\"GraphQL error: {result['errors']}\")","handlingStrategy":"retry","validationCode":"def graphql_request_is_well_formed(query: str, variables: dict) -> bool:\n    return bool(query.strip()) and isinstance(variables, dict)","typeGuard":"def is_graphql_error_body(payload: dict) -> bool:\n    return isinstance(payload, dict) and bool(payload.get(\"errors\"))","tryCatchPattern":"try:\n    data = github_graphql(query, variables)\nexcept RuntimeError as exc:\n    if \"rate limit\" in str(exc).lower():\n        time.sleep(RETRY_DELAY_SECONDS)\n        data = github_graphql(query, variables)\n    else:\n        raise","preventionTips":["Resolve node ids dynamically instead of persisting them","Verify token scopes before scheduled runs","Watch GitHub GraphQL changelogs for ProjectV2 field deprecations","Retry only classified transient errors; permanent ones should fail loudly"],"tags":["github-api","graphql","python","rate-limit","api-auth"],"backgroundTag":null,"analyzedSha":"bc538def4545534201bbfcac4e95ac34ea6501b6","analyzedAt":"2026-08-16T07:30:46.435Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}