TypeError: ‘NoneType’ object is not iterable when server emits response.completed with output=None

当 OpenAI Python SDK 的 client.responses.stream() 连接的是 Codex 兼容后端(如 chatgpt.com/backend-api/codex/responses )时,服务端会在 response.completed 事件里把 response.ou

快速结论:当 OpenAI Python SDK 的 client.responses.stream() 连接的是 Codex 兼容后端(如 chatgpt.com/backend-api/codex/responses)时,服务端会在 response.completed 事件里把 response.output 置为 None,而 SDK 累加器无条件遍历该字段,于是抛出 TypeError: 'NoneType' object is not iterable。优先确认后端是否为 Codex 端点,并检查 SDK 是否已包含对空 output 的防御性处理。

适用环境:Issue 中已确认的信息包括:OpenAI Python SDK(2.24.02.32.0 均可复现,另有 2.38.0 的本地回归用例)、macOS 15(Darwin 25.4.0)、Python 3.11、模型 gpt-5.5gpt-5.4、后端为 ChatGPT Codex 兼容 Responses 端点。Issue 未确认 CUDA、显卡或 PyTorch 版本。

最快修复方案:openai/lib/_parsing/_responses.pyparse_response() 中,把第 61 行的 for output in response.output: 改为 for output in (response.output or []):。该改动由 Issue 报告者本地验证通过,多位评论者也确认可恢复流式调用。

注意事项:这是对本地安装的 SDK 源码打补丁,升级或重装 SDK 后会被覆盖;官方仓库截至该 Issue 关闭时未见合并修复。补丁只解决遍历 None 的问题,真实输出仍需从先前的 response.output_item.done / response.output_text.delta 事件中收集;此外 response.output_text 属性在底层 output 为 null 时也可能抛出同类 TypeError,应用层读取时应单独做防护。

问题场景

用户使用 OpenAI Python SDK 的 client.responses.stream(...) 进行 Responses 流式调用,后端不是官方的 api.openai.com/v1/responses,而是 ChatGPT Codex 后端(Codex CLI / IDE 集成、OAuth token 客户端使用的 https://chatgpt.com/backend-api/codex)。在该后端下,真实输出项通过前置的 response.output_item.done 事件下发,最终 response.completed 事件携带的 response.outputNone。SDK 在累加该完成事件时崩溃,无法走完流式迭代。评论中还提到在 Hermes Agent 网关、长会话(约 168 条 input[])以及全新会话首次调用时均可复现。

报错原文

TypeError: 'NoneType' object is not iterable when server emits response.completed with output=None

File ".../openai/lib/streaming/responses/_responses.py", line 49, in __iter__
  for item in self._iterator:
File ".../openai/lib/streaming/responses/_responses.py", line 57, in __stream__
  events_to_fire = self._state.handle_event(sse_event)
File ".../openai/lib/streaming/responses/_responses.py", line 248, in handle_event
  self.__current_snapshot = snapshot = self.accumulate_event(event)
File ".../openai/lib/streaming/responses/_responses.py", line 360, in accumulate_event
  self._completed_response = parse_response(
File ".../openai/lib/_parsing/_responses.py", line 61, in parse_response
  for output in response.output:
TypeError: 'NoneType' object is not iterable

原因分析

最可能的原因是 Codex 兼容后端的 response.completed SSE 事件不再包含 output 字段(值为 null),而 SDK 的 parse_response() 在没有判断 None 的情况下直接对 response.output 做迭代。官方 api.openai.com 端点会在完成事件里填充 response.output,因此该问题只在 Codex 后端复现。评论者还指出相邻的第二条失败路径:应用层拿到或构造 output=None 的终态响应后,访问 SDK 提供的 response.output_text 辅助属性也可能抛出相同的 TypeError。另有评论提到 SDK 2.24.0tools=None 作为关键字参数传入时也会立即遍历 tools,属于同一类未做空值防护的问题,但 Issue 未给出独立复现证据。

环境排查

  • 确认使用的 OpenAI Python SDK 版本;Issue 中 2.24.02.32.0 均已确认复现,另有 2.38.0 的本地合成回归用例。
  • 确认 Responses 请求实际打到的 base_url / 后端,是否为 chatgpt.com/backend-api/codex 这类 Codex 兼容端点,而非官方 API。
  • 确认触发模型,Issue 中观察到 gpt-5.5gpt-5.4
  • 确认认证方式,Issue 中涉及 Codex OAuth access token 与 ChatGPT Plus OAuth(chatgpt-account 模式)。
  • 确认 Python 版本为 3.11,操作系统为 macOS 15(Darwin 25.4.0)。
  • 确认调用时是否显式传入了值为 Nonetools,评论建议在调用 responses.stream() 前剔除该参数。
  • Issue 未涉及 CUDA、显卡、PyTorch 或自定义 ComfyUI 节点,无需排查这些项。

解决步骤

  1. 先定位崩溃点:在 traceback 中确认最深栈帧是 openai/lib/_parsing/_responses.pyparse_response(),即 for output in response.output: 这一行。
  2. 可优先尝试 Issue 中验证过的补丁:把该行改为 for output in (response.output or []):,让 None 被当作空输出列表处理。评论者反馈这是一处“一字符防护”,改后流式调用恢复正常,SDK 下游的 normalizer 本身已能优雅处理空输出。
  3. 如果不想改动 SDK 源码,可在应用层做防御:捕获 responses.stream() 抛出的这一特定 TypeError,将其视为可恢复的 Codex 流解析失败,回退到原始 responses.create(stream=True) 路径,再从流式事件中收集/回填输出(评论者称 Hermes Agent 侧按此思路做了缓解)。
  4. 调用前剔除 tools=None:评论指出 SDK 2.24.0toolsNone 传入时会主动遍历它,属于同类问题,防御性移除该参数可避免额外触发。
  5. 应用层归一化时,对终态响应的 response.output 显式判空,并把 output is None 与空列表同等对待,从 response.output_item.done / response.output_text.delta 事件回填内容。
  6. 读取 response.output_text 属性时加保护,因为它自身在底层 output 为 null 时也可能抛出同样的 TypeError(评论中给出的 Hermes 侧防护之一)。
  7. 如果使用较新的 SDK 版本,先确认官方是否已合并该防护;Issue 中报告的复现跨度至少覆盖 2.24.02.32.0,评论者认为 SDK 本身应当修复。

验证方法

在应用了 for output in (response.output or []): 补丁后,用 Issue 中的最小复现脚本重新发起 responses.stream(...) 调用:如果流迭代能正常结束、stream.get_final_response() 不再抛 TypeError,即说明崩溃点已消除。同时检查此前的 response.output_item.done 事件是否被正确收集,确保最终输出内容可恢复。评论者建议的回归测试方式为:mock 一个 response.completed 事件且令 response.output = None,断言 parse_response() 返回空输出列表而不是抛异常。

参考来源

openai/openai-python #3312

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 23505

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注