{"record":{"id":"187e8f3c10dc059f","repo":"zed-industries/zed","slug":"graphql-errors-json-dumps-data-errors-300","errorCode":null,"errorMessage":"GraphQL errors: {json.dumps(data['errors'])[:300]}","messagePattern":"GraphQL errors: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"script/github-check-new-issue-for-duplicates.py","lineNumber":104,"sourceCode":"    \"\"\"Search issues, using GitHub's relevance ordering unless a sort is specified.\"\"\"\n    params = {\"q\": query, \"per_page\": per_page}\n    if sort:\n        params.update({\"sort\": sort, \"order\": \"desc\"})\n    return github_api_get(\"/search/issues\", params).get(\"items\", [])\n\n\ndef github_api_graphql(query, variables=None):\n    \"\"\"Run a GraphQL query against the GitHub API, retrying transient failures. \"\"\"\n    url = f\"{GITHUB_API}/graphql\"\n    for attempt in range(3):\n        try:\n            response = requests.post(\n                url, headers=GITHUB_HEADERS, json={\"query\": query, \"variables\": variables or {}}\n            )\n            response.raise_for_status()\n            data = response.json()\n            if \"errors\" in data:\n                raise ValueError(f\"GraphQL errors: {json.dumps(data['errors'])[:300]}\")\n            return data[\"data\"]\n        except requests.RequestException as e:\n            transient = isinstance(e, (requests.ConnectionError, requests.Timeout)) or (\n                isinstance(e, requests.HTTPError) and e.response.status_code in TRANSIENT_HTTP_STATUSES\n            )\n            if not transient or attempt == 2:\n                raise\n            wait = 2 ** attempt\n            log(f\"  Transient GitHub GraphQL error ({e}); retrying in {wait}s\")\n            time.sleep(wait)\n\n\ndef check_team_membership(org, team_slug, username):\n    \"\"\"Check if user is an active member of a team.\"\"\"\n    try:\n        data = github_api_get(f\"/orgs/{org}/teams/{team_slug}/memberships/{username}\")\n        return data.get(\"state\") == \"active\"\n    except requests.HTTPError as e:","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/zed-industries/zed/blob/bc538def4545534201bbfcac4e95ac34ea6501b6/script/github-check-new-issue-for-duplicates.py#L86-L122","documentation":"Raised when the GitHub GraphQL endpoint answers HTTP 200 but the response body contains an \"errors\" array; GitHub reports query-level failures (unresolvable ids, bad variables, missing scopes, rate-limit messages) in a 200 body, not as HTTP errors. The wrapper converts that into a ValueError carrying the first 300 characters of the serialized errors. Because the surrounding except only catches requests.RequestException, this ValueError escapes immediately and is never retried, even when the body error is transient.","triggerScenarios":"POST to {GITHUB_API}/graphql returns 200 with errors, e.g. \"Could not resolve to an Issue with the number of X\" for a wrong/deleted issue number, INSUFFICIENT_SCOPES when GITHUB_TOKEN lacks repo/read:project, a malformed query or wrong variable type after a GitHub schema change, or a secondary rate-limit message embedded in the errors body.","commonSituations":"Expired or under-scoped CI token; querying an issue that was deleted or lives in another repo; GitHub GraphQL schema renames breaking a stored query; heavy CI runs hitting secondary rate limits that surface in the 200 body.","solutions":["Log the full errors array (data['errors'][0]['message'] and ['type']) instead of the 300-char slice, then fix the exact query/variable/id it names","Check the token: expiry date and scopes (repo, read:project) against the data being queried","Reproduce with curl -H \"Authorization: bearer $TOKEN\" -d '{\"query\":\"...\"}' https://api.github.com/graphql to isolate the failing field","If the error message indicates a rate limit, sleep and retry inside the loop instead of letting the ValueError escape (it currently bypasses the retry logic)"],"exampleFix":"// before\nif \"errors\" in data:\n    raise ValueError(f\"GraphQL errors: {json.dumps(data['errors'])[:300]}\")\n\n// after\nif \"errors\" in data:\n    message = str(data['errors'][0].get('message', ''))\n    if 'rate limit' in message.lower() and attempt < 2:\n        wait = 2 ** attempt\n        log(f\"  Transient GraphQL body error; retrying in {wait}s\")\n        time.sleep(wait)\n        continue\n    raise ValueError(f\"GraphQL errors: {json.dumps(data['errors'])[:300]}\")","handlingStrategy":"retry","validationCode":"def graphql_request_is_well_formed(query: str, variables: dict | None) -> bool:\n    return bool(query and query.strip()) and (variables is None or isinstance(variables, dict))","typeGuard":"def is_graphql_error_body(payload: dict) -> bool:\n    return isinstance(payload, dict) and isinstance(payload.get(\"errors\"), list) and len(payload[\"errors\"]) > 0","tryCatchPattern":"try:\n    data = github_api_graphql(query, variables)\nexcept ValueError as exc:\n    # ValueError escapes the wrapper's RequestException handler, so classify here\n    text = str(exc)\n    if \"rate limit\" in text.lower():\n        time.sleep(5)\n        data = github_api_graphql(query, variables)\n    else:\n        raise","preventionTips":["Check the rate limit with a cheap query before batch jobs","Validate node ids and issue numbers before embedding them in queries","Keep the token scoped but sufficient: repo and read:project","Log the full errors array rather than a 300-char slice"],"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"}