{"record":{"id":"39431ef75820d7d8","repo":"zed-industries/zed","slug":"graphql-errors-data-errors","errorCode":null,"errorMessage":"GraphQL errors: {data['errors']}","messagePattern":"GraphQL errors: (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"script/github-track-duplicate-bot-effectiveness.py","lineNumber":170,"sourceCode":"\n\ndef parse_suggested_issues(comment_body):\n    \"\"\"Extract issue numbers from the bot's comment (lines like '- #12345').\"\"\"\n    return [int(match) for match in re.findall(r\"^- #(\\d+)\", comment_body, re.MULTILINE)]\n\n\ndef github_api_graphql(query, variables=None, partial_errors_ok=False):\n    \"\"\"Execute a GitHub GraphQL query. Raises on errors unless partial_errors_ok is set.\"\"\"\n    response = requests.post(\n        GRAPHQL_URL,\n        headers=GITHUB_HEADERS,\n        json={\"query\": query, \"variables\": variables or {}},\n    )\n    response.raise_for_status()\n    data = response.json()\n    if \"errors\" in data:\n        if not partial_errors_ok or \"data\" not in data:\n            raise RuntimeError(f\"GraphQL errors: {data['errors']}\")\n        print(f\"  GraphQL partial errors (ignored): {data['errors']}\")\n    return data[\"data\"]\n\n\ndef find_canonical_among(duplicate_number, candidates):\n    \"\"\"Check if any candidate issue has duplicate_number marked as a duplicate.\n\n    The MarkedAsDuplicateEvent lives on the canonical issue's timeline, not the\n    duplicate's. So to find which canonical issue our duplicate was closed against,\n    we check each candidate's timeline for a MarkedAsDuplicateEvent whose\n    `duplicate` field matches our issue.\n\n    Returns the matching canonical issue number, or None.\n    \"\"\"\n    if not candidates:\n        return None\n\n    # candidate issue numbers are baked into the query body via field aliases","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/zed-industries/zed/blob/bc538def4545534201bbfcac4e95ac34ea6501b6/script/github-track-duplicate-bot-effectiveness.py#L152-L188","documentation":"This helper posts a GraphQL query once (no HTTP retry wrapper) and raises when the 200 body contains \"errors\", unless partial_errors_ok is set and the body still carries usable data — in that tolerated case it prints the errors and returns the partial data. So the raise means either the call was strict, or partial mode was requested but the failure was total (no data key, e.g. data: null).","triggerScenarios":"Fan-out timeline queries (find_canonical_among) hit issues that error individually; with partial_errors_ok=False any body error raises; with partial_errors_ok=True a wholly failed query (errors plus data:null, e.g. querying a deleted issue node) still raises because there is nothing to return.","commonSituations":"Candidate issues deleted or made private mid-run; token lacking scope for MarkedAsDuplicateEvent timeline items; callers forgetting to pass partial_errors_ok on queries designed to tolerate nulls.","solutions":["Inspect data['errors'] in the message: it names the node and failure type","Pass partial_errors_ok=True for fan-out queries where individual nulls are acceptable, and null-check each node before use","Filter out deleted/inaccessible candidates before querying their timelines","Wrap the single POST with the same transient-status retry the other scripts use"],"exampleFix":"// before\ndata = github_api_graphql(query, variables)\nnodes = data[\"node\"][\"timelineItems\"][\"nodes\"]\n\n// after\ndata = github_api_graphql(query, variables, partial_errors_ok=True) or {}\nnodes = ((data.get(\"node\") or {}).get(\"timelineItems\") or {}).get(\"nodes\") or []","handlingStrategy":"fallback","validationCode":"def candidates_are_queryable(candidates: list) -> bool:\n    return all(c.get(\"number\") and c.get(\"node_id\") for c in candidates)","typeGuard":"def graphql_response_has_data(payload: dict) -> bool:\n    return isinstance(payload.get(\"data\"), dict)","tryCatchPattern":"try:\n    data = github_api_graphql(query, variables, partial_errors_ok=True)\nexcept RuntimeError:\n    data = {}  # tolerate total failure of one candidate; skip it downstream\nnodes = ((data.get(\"node\") or {}).get(\"timelineItems\") or {}).get(\"nodes\") or []","preventionTips":["Pass partial_errors_ok for fan-out queries over many optional nodes","Null-check every node in partial results before use","Drop deleted/inaccessible candidates before querying them","Log tolerated partial errors so silent data loss stays visible"],"tags":["github-api","graphql","partial-errors","python"],"backgroundTag":null,"analyzedSha":"bc538def4545534201bbfcac4e95ac34ea6501b6","analyzedAt":"2026-08-16T07:30:46.435Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}