zed-industries/zed · error · FetchError

Sentry API returned HTTP {error.code} for {path}: {detail}

Error message

Sentry API returned HTTP {error.code} for {path}: {detail}

What it means

Raised by api_get() in script/select-sentry-crash-candidates when urllib raises HTTPError — the Sentry API answered with a 4xx/5xx. The message embeds the status code plus the 'detail' field of Sentry's JSON error body when it can be parsed, falling back to the raw body.

Source

Thrown at script/select-sentry-crash-candidates:61

    return None


def api_get(path: str, token: str):
    url = f"{SENTRY_BASE_URL}{path}"
    request = urllib.request.Request(url)
    request.add_header("Authorization", f"Bearer {token}")
    request.add_header("Accept", "application/json")

    try:
        with urllib.request.urlopen(request, timeout=30) as response:
            return json.loads(response.read().decode("utf-8"))
    except urllib.error.HTTPError as error:
        body = error.read().decode("utf-8", errors="replace")
        try:
            detail = json.loads(body).get("detail", body)
        except (json.JSONDecodeError, AttributeError):
            detail = body
        raise FetchError(f"Sentry API returned HTTP {error.code} for {path}: {detail}")
    except urllib.error.URLError as error:
        raise FetchError(f"Failed to connect to Sentry API: {error.reason}")


def fetch_issues(token: str, organization: str, limit: int, query: str):
    encoded_query = urllib.parse.quote(query)
    path = (
        f"/organizations/{organization}/issues/"
        f"?limit={limit}&sort=freq&query={encoded_query}"
    )
    return api_get(path, token)


def fetch_latest_event(token: str, issue_id: str):
    return api_get(f"/issues/{issue_id}/events/latest/", token)


def parse_int(value, fallback=0) -> int:

View on GitHub (pinned to bc538def45)

Solutions

  1. Verify the token and org: curl -H "Authorization: Bearer $SENTRY_TOKEN" "https://sentry.io/api/0/organizations/$SENTRY_ORG/" should return 200
  2. Recreate the token with org:read, project:read and event:read scopes
  3. On 400, simplify the query string and validate Sentry search syntax against the issues endpoint
  4. On 429, add backoff honoring X-RateLimit-Remaining/Retry-After before retrying

Example fix

# before
path = (f"/organizations/{organization}/issues/"
        f"?limit={limit}&sort=freq&query={encoded_query}")
return api_get(path, token)

# after: surface scopes hint on the common auth failure
try:
    return api_get(path, token)
except FetchError as error:
    if "HTTP 401" in str(error):
        raise FetchError("token rejected (check SENTRY_TOKEN and its scopes)") from error
    raise
Defensive patterns

Strategy: try-catch

Validate before calling

# Pre-flight token/org before the real queries
def validate_sentry_access(token: str, org: str) -> None:
    path = f"/organizations/{org}/"
    try:
        api_get(path, token)
    except FetchError as error:
        raise SystemExit(f"pre-flight failed: {error}") from error

Try / catch

try:
    issues = fetch_issues(token, org, limit, query)
except FetchError as error:
    text = str(error)
    if "HTTP 401" in text or "HTTP 403" in text:
        sys.exit("SENTRY_TOKEN invalid or missing scopes")
    if "HTTP 404" in text:
        sys.exit(f"org {org!r} or issue not found")
    if "HTTP 429" in text:
        time.sleep(backoff); retry()  # honor Retry-After
    else:
        raise

Prevention

When it happens

Trigger: GET /organizations/{org}/issues/?limit=...&sort=freq&query=... or /issues/{id}/events/latest/ returning 401 (invalid/expire token), 403 (token lacks org:read / event:read scope), 404 (wrong org slug or issue id), 400 (invalid search query syntax), or 429 (rate limited).

Common situations: SENTRY_TOKEN unset, revoked, or created without the needed scopes; SENTRY_ORG env var is the org name instead of the slug (or vice versa); search query uses fields not valid for the issues endpoint; looping over many issues without caching triggers 429.

Related errors


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