anthropics / anthropics/anthropic-sdk-python

Mid-stream `error` events still raise `APIStatusError` with `status_code=200` (#1258 closed without a fix)

未关闭
#1,918 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看
主要语言
Python
星标
3.9k
派生
853
平均合并
1 天 18 小时
30 天内合并 PR
11

描述

**Correction (same day):** #1258 was not closed without a fix. #1587 added `APIStatusError.type`, which carries the in-stream error type (`overloaded_error` here), and the maintainers chose that over synthesizing a status code. What this issue is really about is the class: an in-stream error is still the bare `APIStatusError`, never `OverloadedError` / `RateLimitError`, so class-based handling misses it. The status code staying 200 is by design. PR that only picks the class from `error.type`: https://github.com/anthropics/anthropic-sdk-python/pull/1921

---

Follow-up to #1258, closed as completed on 2026-03-31. The behaviour is unchanged on 0.122.0, 1.4.0 and main.

When the API sends an `error` event inside a stream, `_streaming.py` passes the original 200 response to `_make_status_error`, which dispatches on `response.status_code` only, so the result is a bare `APIStatusError` with `status_code=200`. For an `overloaded_error` that means `OverloadedError` (529) is never raised in-stream, and anything that keys retries on the exception class or the status code sees a 200 "error". Wrappers end up with `"overloaded_error" in str(e)` (agno does exactly that) because nothing else is stable.

Offline repro, no network:

```python
import platform
import sys

import anthropic

try:
import httpx2 as httpx # anthropic >= 1.0
except ImportError:
import httpx

SSE = (
'event: message_start\ndata: {"type":"message_start","message":{"id":"msg_1","type":"message","role":"assistant","model":"claude-sonnet-4-6","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":1,"output_tokens":0}}}\n\n'
'event: error\ndata: {"type":"error","error":{"type":"overloaded_error","message":"Overloaded"}}\n\n'
)

def handler(request: httpx.Request) -> httpx.Response:
return httpx.Response(200, headers={"content-type": "text/event-stream"}, content=SSE.encode())

client = anthropic.Anthropic(api_key="x", http_client=httpx.Client(transport=httpx.MockTransport(handler)), max_retries=0)
print(f"python {sys.version.split()[0]} {platform.system()}, anthropic {anthropic.__version__}")
try:
with client.messages.stream(model="claude-sonnet-4-6", max_tokens=8, messages=[{"role": "user", "content": "hi"}]) as s:
for _ in s:
pass
except anthropic.APIStatusError as e:
print(f"raised {type(e).__name__} status_code={e.status_code} body={e.body}")
print(f"has OverloadedError class: {hasattr(anthropic, 'OverloadedError')}")
```

Output on 0.122.0 and 1.4.0 (python 3.12.13, macOS):

```
raised APIStatusError status_code=200 body={'type': 'error', 'error': {'type': 'overloaded_error', 'message': 'Overloaded'}}
has OverloadedError class: True
```

Expected: the in-stream error mapped from `body["error"]["type"]` (overloaded_error to `OverloadedError` / 529, rate_limit_error to `RateLimitError` / 429, api_error to `InternalServerError` / 500, and so on), with the 200 response still attached. #1263 and #1269 did roughly that and were closed unmerged. Happy to send a fresh PR in whatever shape the "better approach" mentioned in #1258 was meant to take.

贡献指南

打开贡献指南

评估

这个 Issue 还没有评估数据。

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。