{"record":{"id":"7ccac7a8202b3d7c","repo":"zed-industries/zed","slug":"sentry-api-returned-http-err-code-for-path-d","errorCode":null,"errorMessage":"Sentry API returned HTTP {err.code} for {path}: {detail}","messagePattern":"Sentry API returned HTTP (.+?) for (.+?): (.+?)","errorType":"exception","errorClass":"FetchError","httpStatus":null,"severity":"error","filePath":"script/sentry-fetch","lineNumber":108,"sourceCode":"    return None\n\n\ndef api_get(path, token):\n    \"\"\"Make an authenticated GET request to the Sentry API.\"\"\"\n    url = f\"{SENTRY_BASE_URL}{path}\"\n    req = urllib.request.Request(url)\n    req.add_header(\"Authorization\", f\"Bearer {token}\")\n    req.add_header(\"Accept\", \"application/json\")\n    try:\n        with urllib.request.urlopen(req) as response:\n            return json.loads(response.read().decode(\"utf-8\"))\n    except urllib.error.HTTPError as err:\n        body = err.read().decode(\"utf-8\", errors=\"replace\")\n        try:\n            detail = json.loads(body).get(\"detail\", body)\n        except (json.JSONDecodeError, AttributeError):\n            detail = body\n        raise FetchError(f\"Sentry API returned HTTP {err.code} for {path}: {detail}\")\n    except urllib.error.URLError as err:\n        raise FetchError(f\"Failed to connect to Sentry API: {err.reason}\")\n\n\ndef resolve_issue(identifier, token):\n    \"\"\"Resolve a Sentry issue by short ID or numeric ID.\n\n    Returns (issue_id, short_id, issue_data).\n    \"\"\"\n    if identifier.isdigit():\n        issue = api_get(f\"/issues/{identifier}/\", token)\n        return identifier, issue.get(\"shortId\", identifier), issue\n\n    result = api_get(f\"/organizations/{DEFAULT_SENTRY_ORG}/shortids/{identifier}/\", token)\n    group_id = str(result[\"groupId\"])\n    issue = api_get(f\"/issues/{group_id}/\", token)\n    return group_id, identifier, issue\n","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/zed-industries/zed/blob/bc538def4545534201bbfcac4e95ac34ea6501b6/script/sentry-fetch#L90-L126","documentation":"Raised by api_get() in script/sentry-fetch when the Sentry API returns a 4xx/5xx (urllib HTTPError). Unlike its counterpart in select-sentry-crash-candidates, this urlopen call sets no timeout, so the HTTPError path is reached only after the server actually responded with an error status. The detail is Sentry's JSON 'detail' when parseable, else the raw body.","triggerScenarios":"resolve_issue() hitting /issues/{id}/ with an unknown numeric id (404); /organizations/{DEFAULT_SENTRY_ORG}/shortids/{short}/ with a short ID from another org (404); /issues/{id}/events/latest/ on a deleted issue (404); 401 from a revoked token; 403 from a token missing event:read scope.","commonSituations":"DEFAULT_SENTRY_ORG in the script does not match where the crash issue lives; token expired between cron runs; issue was resolved-and-deleted in Sentry before the fetch; typo'd short ID like ABC-123 vs the plain short part.","solutions":["Confirm the issue exists in the org the script targets (DEFAULT_SENTRY_ORG) via the Sentry UI","For short IDs, pass the exact short_id shown by Sentry for that org","Refresh the SENTRY_TOKEN and confirm event:read/org:read scopes","Treat 404 as 'issue gone' and skip it rather than aborting the whole batch"],"exampleFix":"# before\nif identifier.isdigit():\n    issue = api_get(f\"/issues/{identifier}/\", token)\n\n# after: distinguish gone-vs-auth failures for batch runs\ntry:\n    issue = api_get(f\"/issues/{identifier}/\", token)\nexcept FetchError as error:\n    if \"HTTP 404\" in str(error):\n        log(f\"issue {identifier} no longer exists; skipping\")\n        continue\n    raise","handlingStrategy":"try-catch","validationCode":"# Validate identifier shape before resolving\nif not (identifier.isdigit() or (\"-\" in identifier and len(identifier.split(\"-\")) == 2)):\n    sys.exit(f\"not a numeric id or SHORTID: {identifier!r}\")","typeGuard":"def is_numeric_issue_id(identifier: str) -> bool:\n    return identifier.isdigit()","tryCatchPattern":"try:\n    issue_id, short_id, issue = resolve_issue(identifier, token)\nexcept FetchError as error:\n    if \"HTTP 404\" in str(error):\n        print(f\"{identifier}: not found in org {DEFAULT_SENTRY_ORG}; skipping\")\n        continue\n    if \"HTTP 401\" in str(error):\n        sys.exit(\"SENTRY_TOKEN rejected — refresh it\")\n    raise","preventionTips":["Keep issue ids from the same org the script targets (DEFAULT_SENTRY_ORG)","Treat 404 as skippable in batch runs instead of fatal","Refresh tokens on a schedule so cron runs do not die on 401"],"tags":["python","sentry","http","api","cli"],"backgroundTag":null,"analyzedSha":"bc538def4545534201bbfcac4e95ac34ea6501b6","analyzedAt":"2026-08-16T07:30:46.435Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}