快速结论:这个报错通常出现在使用 OpenAI Python SDK 消费流式响应(stream=True)时,如果流中途发生读取超时或连接中断,SDK 没有包装异常,而是直接把原始 httpx 异常抛出;优先排查你的异常捕获是否只写了 except openai.APIError,以及你是否依赖 max_retries 对中途失败的流进行重试。
适用环境:Issue 中确认的环境为 macOS、Python 3.12.13;受影响库版本为 openai v2.26.0(搭配 httpx 0.28.1)和 openai v3.8.0(搭配 httpx 2.12.0)。
最快修复方案:Issue 中已验证的修复来自 PR #3827,修复后流读取失败会抛出 APITimeoutError 或 APIConnectionError,并将原始异常保留为 __cause__。升级到包含该修复的版本即可。
注意事项:该修复只解决异常包装问题,不会自动重试已部分消费的流。因此即使升级后,流中途失败仍可能需要你自己重建请求并重新消费,不能依赖 max_retries 自动补齐流式请求。
问题场景
用户通过 OpenAI Python SDK 发起流式请求,例如 client.chat.completions.create(..., stream=True),并在循环中逐块消费响应。当流在传输过程中发生读取超时、连接中断或响应体解码错误时,异常会直接逃逸到用户代码。Issue 中的复现脚本使用 MockTransport,先正常产出一个 SSE 数据块,随后在迭代中抛出 httpx.ReadTimeout,从而模拟流中途超时。
报错原文
escaped: httpx.ReadTimeout: timed out while reading the stream
isinstance(e, openai.APIError) = False
requests made with max_retries=2: 1
escaped: httpx2.ReadTimeout: timed out while reading the stream
isinstance(e, openai.APIError) = False
requests made with max_retries=2: 1
原因分析
根据 Issue 描述,_base_client 会在首次发送请求时包装传输层异常:httpx.TimeoutException 转为 APITimeoutError,其他传输错误转为 APIConnectionError,并进入重试循环。但一旦响应进入流式阶段,_streaming.py 在迭代响应时没有做同等处理,因此读取超时或连接中断会以原始 httpx 异常形式抛出。这导致两个直接后果:
- 异常不属于
openai.APIError,常见的except openai.APIError无法捕获; max_retries不参与流式阶段,复现中设置max_retries=2但实际只发出一次请求。
Issue 还指出 anthropic-sdk-python 存在相同的生成基础代码缺陷,但这不影响本仓库的定位。
环境排查
- 确认
openai版本:Issue 中复现于 v2.26.0 和 v3.8.0。 - 确认 httpx 版本及命名空间:httpx 0.28.1 对应
httpx.*,httpx 2.12.0 对应httpx2.*。 - 确认 Python 版本:Issue 为 3.12.13,建议保持一致以复现问题。
- 确认调用方式是否为流式:
stream=True并显式迭代响应对象。 - 确认异常捕获方式:是否只捕获了
openai.APIError。 - 确认
max_retries设置:如果依赖它在流中途失败后重试,需要确认实际请求次数。 - 确认是否使用了自定义
http_client或 MockTransport,此类自定义传输层更容易复现流中异常。
解决步骤
- 升级到包含 PR #3827 修复的 openai 版本。该 PR 覆盖同步/异步流中途的超时、连接错误和解码错误,并保留原有 Assistants 流式行为。
- 升级后,流读取失败将抛出
APITimeoutError或APIConnectionError,而不是原始httpx.ReadTimeout或httpx.RemoteProtocolError。 - 如果升级后仍需要定位根本原因,检查异常的
__cause__,修复会将原始异常保留在该属性中。 - 如果你的代码中只捕获
openai.APIError,升级后即可正常捕获;但在升级前,可优先尝试同时捕获对应的 httpx 异常类型作为过渡。此处为推测性做法,未在 Issue 中明确验证。 - 对于流中途失败后是否重试,需要由你的业务逻辑决定。修复明确说明不会自动重试已部分消费的流。
验证方法
首先确认 openai 版本已包含 #3827 修复。然后重跑 Issue 中的复现脚本或等价的流式请求,观察异常类型是否变为 openai.APITimeoutError 或 openai.APIConnectionError,并确认 isinstance(e, openai.APIError) 返回 True。同时检查 max_retries 行为是否符合预期:修复不会对已部分消费的流失效重试,因此 requests_seen 仍应只计一次。
参考来源
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。


