Codex 报 websocket closed by server before response.completed:先别慌,Codex 会先自己重连
在 Codex 里跑任务,界面上冒出来这么一行:
stream disconnected before completion: websocket closed by server before response.completed
有时候前面还带着 Reconnecting... 2/5,有时候后面跟着一句 Falling back from WebSockets to HTTPS transport.
看到「disconnected」「closed」,第一反应多半是任务挂了。但按 Codex 的源码,这条提示出现时,Codex 会先自己想办法:重连、再换通道。这篇把它的来龙去脉拆开,告诉你什么时候可以不管,什么时候要动手。
本文依据 Codex 仓库 rust-v0.157.1(2026-09-25 发布)的源码。
一、这句话字面上在说什么
Codex 连 OpenAI 的时候,默认不是走普通的 HTTP 请求,而是开一条 WebSocket 长连接,模型的回答顺着这条连接一块块推过来。回答推完之后,服务端会发一个 response.completed 事件,告诉 Codex「这一轮说完了」。
这句报错的意思就是:服务端在发出 response.completed 之前,主动把 WebSocket 关了。
源码里它出现在 codex-api 读 WebSocket 消息的那段循环里:只要在收到完成事件之前读到了关闭帧,就生成这条错误。前面的 stream disconnected before completion: 是 Codex 给所有「流没走完就断了」的错误统一加的前缀。
所以它和你可能见过的 stream closed before response.completed 属于同一类:都是没等到 response.completed 流就断了。那一条的排查思路见文末相关阅读。
二、谁会走 WebSocket,谁不会
这一点决定了你会不会遇到它:
- Codex 内置的 OpenAI 提供方(用 ChatGPT 账号登录,或者用 OpenAI API Key 直连),默认开启 WebSocket。
- 你在
config.toml里自定义的提供方([model_providers.xxx]),默认不开,除非你显式写了supports_websockets = true。
换句话说,如果你是通过自定义提供方接的第三方模型,又没开这个开关,那你不会见到这条报错;见到了,说明走的是开了 WebSocket 的通道,通常就是内置 OpenAI。
三、它出现以后,Codex 自己会做什么
这是最值得知道的部分。源码里对这类流中断的处理分三步:
第一步,原地重连。 默认最多重连 5 次(stream_max_retries,默认 5,可配置的上限是 100)。每次重连会在界面上提示 Reconnecting... 当前次数/总次数。正式版会隐藏第一次重连的提示,所以你看到的往往是从 2/5 开始的。
第二步,换通道。 5 次都失败以后,Codex 不会马上报错,而是发一条警告:
Falling back from WebSockets to HTTPS transport.
然后把重试计数清零,改用普通的 HTTPS 流式请求,重新开始一轮重试。
第三步,这个会话里不再用 WebSocket。 源码注释写得很明白:切到 HTTPS 以后,这个会话剩下的所有轮次都走 HTTPS,不会再切回来。
所以,如果你看到的是带 Reconnecting... 的提示,或者 Falling back... 那条警告,任务并没有停,Codex 正在自救。等一等,大多数时候它会接着往下跑。
四、什么时候需要你动手
真正需要处理的是这两种情况:
情况一:切到 HTTPS 之后还是失败,这一轮最终报错了。 这时问题可能已经不在 WebSocket 本身,而在更底层的网络:代理不稳、公司网关掐长连接、网络本身在抖。WebSocket 只是先暴露出来的那个。按普通断流去查,见相关阅读里的断流排查。
情况二:每个新会话一开始都要来一遍「重连 5 次 → 切 HTTPS」。 能用,但每次都白白浪费一段重试时间。这说明你所在的网络对 WebSocket 长连接不友好,比如某些代理只放行普通 HTTPS 请求。
情况二可以先用 Codex 自带的体检命令确认:
codex doctor
输出里有一项 websocket 检查(内部名 network.websocket_reachability),会显示当前提供方是否启用了 WebSocket、以及 WebSocket 能不能连通。如果这一项给出警告(握手失败或超时时,doctor 会提示 “HTTPS fallback may still work”,即 HTTPS 回退可能仍然可用),而其它网络检查正常,就很可能是 WebSocket 被网络环境拦了。codex doctor 其它各项怎么看,见相关阅读。
五、两个容易想错的地方
一、别急着重装 Codex。 这条报错说的是连接被服务端关了,跟本地安装没关系。重装不会改变你的网络环境,也不会改变服务端的行为。
二、「重连成功了」不代表网络没问题。 如果你频繁看到 Reconnecting...,即使最后都成功了,也说明链路不稳。长任务里每次重连都要重新等响应,累积起来会明显拖慢速度。能换一个更稳的网络,收益比调参数大。
附:和这条报错相关的几个配置项
如果你用的是自定义提供方,下面这些都写在 config.toml 的 [model_providers.<名字>] 段里,默认值取自源码:
| 配置项 | 默认值 | 作用 |
|---|---|---|
supports_websockets | false(内置 OpenAI 为开启) | 这个提供方是否走 WebSocket |
stream_max_retries | 5,最大可设 100 | 流断开后原地重连的次数 |
stream_idle_timeout_ms | 300000(5 分钟) | 流里多久没有新数据,就当连接已经断了 |
websocket_connect_timeout_ms | 15000(15 秒) | 建立 WebSocket 连接最多等多久 |
request_max_retries | 4 | 发往这个提供方的 HTTP 请求本身失败时的重试次数 |
其中最容易被忽略的是 stream_idle_timeout_ms。它的意思是:连接没断,但 5 分钟一个字都没回来,Codex 也会认为流已经断了,然后进入重连。所以如果你遇到的是「界面卡了好几分钟,然后才冒出断流提示」,有可能是这个空闲超时在起作用,而不是连接一开始就断了。
调这些数字之前想清楚:把重连次数调大,只是让 Codex 更有耐心,不会让网络变好。如果每次都要重连好几次才成功,更该做的是换一个稳的网络,或者查代理对长连接的处理。
附:怎么判断问题出在你这边还是服务端
一个简单的办法是换一台设备或一个网络对照:同一时间,用手机热点跑一个最简单的 Codex 任务。热点下正常、原网络下频繁断,问题在你的网络或代理;两边都断,更可能是服务端当时不稳,这时去看 OpenAI 的官方状态页,比折腾本地配置有用。
六、一句话总结
- 看到
Reconnecting...或Falling back from WebSockets to HTTPS transport.:Codex 还在自救,先等; - 切到 HTTPS 还是失败:按普通断流查网络;
- 每个会话开头都要切一次:跑
codex doctor看 WebSocket 那一项,排查网络环境是否拦了长连接。
相关阅读
相关阅读
- Codex 会话管理实战:归档、取消归档、删除与按名字找回
- 版本号自己变了:Codex CLI 自动更新与版本口径排查
- 本地跑还是云端跑:Codex 选型先看模型可用性
- Codex 报 ChatGPT login is disabled. Use API key login instead. 是谁禁的?按这个顺序挨个查
- Codex 报 remote compaction v2 expected exactly one compaction output item 怎么办
- Codex 报 Selected model is at capacity. Please try a different model. 怎么办
留言讨论
评论发布后会被人工复核,违规内容将被删除。
如果发表没有反应,可以前往联系我们告诉我们。