文章来源:mp.weixin.qq.com/s/Ww3_nSGSR…
其他问题源:community.openai.com/t/stream-di…
最近在折腾 Codex CLI 的时候,我碰到了一个很烦的问题。
表面上看一切正常:能登录、能看 /status、额度也没问题。
但只要真正发起一次对话,比如输入一句最简单的 hello,它就直接报错:
stream disconnected before completion: stream closed before response.completed
一开始,我几乎下意识地把问题归因到网络上。
毕竟这种报错看起来太像“连接断了”:
- 回复没出来
- 输出中途断掉
- 多试几次还是不行
于是我开始反复切换网络,Wi-Fi、热点、不同环境来回测。
结果折腾了一圈,问题依旧。
最后排查下来才发现:真正的根因根本不是网络,而是 Codex CLI 里遗留的一段 provider 配置。
这篇文章,就把这次排查过程完整记录下来。
如果你也遇到过 Codex CLI 莫名断流、一直 stream disconnected 的情况,希望这篇能帮你少踩一点坑。
问题现象:看起来像网络,其实又不完全像
先说现象。
我在 Codex CLI 里执行 /status,看到的大部分信息都很正常:
- 模型正常
- 账号正常
- 配额正常
- 没有明显的限流迹象
但只要真正开始对话,就会报这个错:
stream disconnected before completion: stream closed before response.completed
这种错误最容易让人联想到几种原因:
- 本地网络不稳定
- 账号被限流
- 服务端暂时波动
- 客户端版本有问题
我最开始也是这么想的。
但继续观察后,我发现几个地方不太对:
- 我已经换了很多次网络,问题没有任何改善
/status里额度是正常的,并不是超限- 请求不是一开始就失败,而是在返回内容的过程中被中断
这其实是个很关键的区别。
如果真的是网络完全不通,往往是连请求都发不出去;
而我这里是:
- CLI 能正常打开
/status能正常显示- 请求也能发出去
- 只是响应流在中途被关闭了
换句话说,这不像“根本连不上”,更像是:
连接建立了,请求发出去了,服务端也开始回了,但回到一半断了。
到这里我就意识到,问题可能并不在“网络能不能连通”,而在于流式返回这一层。
第一个关键线索:/status 里那行被忽略的 provider
接下来,真正让我开始锁定方向的,是 /status 里的一行信息:
Model provider: custom - https://api.codemirror.codes
这行当时一眼看过去,其实很容易忽略。
但仔细一想,它非常关键。
因为它说明了一件事:
我现在的 Codex CLI,并不是在走默认 provider,而是在走一个自定义的 provider。
而且这个 provider 还明确指向了一个地址:
https://api.codemirror.codes
这时候我的思路一下子收紧了。
如果当前并不是官方默认 provider,而是一个 custom provider,那断流问题就很可能和下面这些有关:
- 第三方中转层
- 自定义网关
- 协议兼容性
- 流式响应实现不完整
- CLI 与 provider 之间的接口行为不一致
也就是说,问题未必是“我网络不好”,也可能是:
这个 provider 本身能接请求,但对流式输出的支持并不稳定。
第二个关键线索:配置文件里果然有一段 custom provider
既然 /status 已经提示我当前使用的是 custom provider,那下一步就很自然了:
去看 Codex CLI 的配置文件。
我检查了本地的:
cat ~/.codex/config.toml
打开之后,马上就看到了问题所在:
disable_response_storage = true
model = "gpt-5.3-codex"
model_provider = "custom"
model_reasoning_effort = "medium"
personality = "pragmatic"
[features]
multi_agent = true
[model_providers.custom]
base_url = "https://api.codemirror.codes"
name = "custom"
requires_openai_auth = true
wire_api = "responses"
看到这里,事情基本已经明朗了。
其中最关键的是这两部分。
第一句:
model_provider = "custom"
它明确告诉 Codex CLI:
不要走默认 provider,走我定义的 custom。
第二段:
[model_providers.custom]
base_url = "https://api.codemirror.codes"
它进一步说明:
这个 custom provider 的目标地址,就是前面 /status 里显示的那个 base_url。
到这里,其实已经可以下一个阶段性结论了:
不是 Codex CLI 自己随机抽风,而是它一直在按照本地配置,去走一个自定义 provider。
这里有个很容易误会的点:这不是 Codex 默认自动生成的
这部分我觉得特别值得单独说一下,因为它很容易让人误解。
看到 custom 和 base_url 的时候,很多人第一反应可能是:
难道 Codex CLI 默认安装完就会帮我配置一个第三方 provider?
我一开始也有点往这个方向怀疑。
但后来确认后发现,并不是。
真正的情况是:
这个
custom的base_url,其实是我之前自己配置过的中转站地址。
也就是说,这不是 Codex 官方默认给我写进去的,也不是安装时自动生成的神秘配置,而是:
- 我以前为了别的用途配过中转
- 或者某次调试时写过自定义 provider
- 之后这份配置一直保留在
~/.codex/config.toml里 - 现在重新用 Codex CLI 时,它就继续生效了
这一点非常重要。
因为如果不把这点说清楚,读者很容易误会成:
“Codex CLI 默认就会把请求发到第三方站点。”
这显然不是事实。
更准确的说法应该是:
如果你曾经在本地配置过 custom provider,那么 Codex CLI 会继续沿用那份配置。
所以如果你也在自己的配置文件里看到类似内容:
model_provider = "custom"
[model_providers.custom]
base_url = "https://某个地址"
那么请先别急着怪客户端,先问问自己:
- 这个地址是不是我以前配过的中转?
- 是不是我之前接过某个兼容 OpenAI 接口的网关?
- 是不是我复制过别人的配置模板?
- 是不是这个机器上保留了旧的 CLI 配置?
很多时候,问题就藏在这里。
验证思路:把 custom provider 去掉,看看能不能恢复
既然怀疑点已经比较明确,那接下来最好的方式不是继续猜,而是做一个最简单的验证:
把
custom provider配置去掉,重新测试。
我把配置改成只保留基础项,删除了和 custom 相关的部分。
修改后大致是这样:
disable_response_storage = true
model = "gpt-5.3-codex"
model_reasoning_effort = "medium"
personality = "pragmatic"
[features]
multi_agent = true
[projects."/Applications"]
trust_level = "trusted"
[projects."/Users/zhangyusheng"]
trust_level = "untrusted"
[shell_environment_policy]
inherit = "core"
[shell_environment_policy.set]
CLAUDE_CODE_ATTRIBUTION_HEADER = "0"
CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS = "1"
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = "true"
ENABLE_TOOL_SEARCH = "false"
本质上做了两件事。
第一,删除这句:
model_provider = "custom"
第二,删除整个自定义 provider 配置块:
[model_providers.custom]
base_url = "https://api.codemirror.codes"
name = "custom"
requires_openai_auth = true
wire_api = "responses"
然后重启 Codex CLI,再测一次。
结果出来了:问题立刻消失
配置改完之后,我重新启动 Codex CLI,先执行 /status。
这时候最明显的变化是:
原来那行
Model provider: custom - https://api.codemirror.codes
已经没有了。
接着我再发一句最简单的话:
hello
这一次,终于正常返回了。
之前反复出现的:
stream disconnected before completion
也没有再出现。
到这里,问题就算彻底坐实了:
真正的根因不是网络,不是额度,不是账号,而是
~/.codex/config.toml里遗留的 custom provider 配置。
这次排查里,我觉得最值得记住的一个判断方法
这次让我感触挺深的一点是:
很多技术问题,表面现象和真实根因并不总是一致。
这次的报错,第一眼真的太像网络问题了。
如果只看“输出断掉”这个现象,很容易一直在网络层打转。
但实际上,真正更有价值的判断应该是:
- 能不能正常打开 CLI?
- 能不能正常查看
/status? - 请求是不是已经成功发出了?
- 是在建立连接前失败,还是在流式输出时失败?
如果是像我这次这样:
- CLI 正常
- 状态正常
- 请求发得出去
- 但在返回途中断掉
那就要优先怀疑下面这些方向,而不是只盯着网络:
- 自定义 provider
- 第三方中转层
- 协议兼容问题
- 流式响应实现问题
- 本地残留配置
这个思路上的转弯,其实比具体命令还重要。
如果你也遇到了类似问题,可以按这个顺序查
这里我把这次排查过程,整理成一个更通用的思路。
1. 先看 /status
重点关注这行:
Model provider: ...
如果显示的是类似:
custom - https://某个地址
那一定要提高警惕。
因为这说明你当前不是在走默认 provider。
2. 检查 ~/.codex/config.toml
直接看配置文件:
cat ~/.codex/config.toml
重点搜索这些关键词:
grep -nE 'model_provider|model_providers|base_url|wire_api|env_key' ~/.codex/config.toml
如果你看到了:
model_provider = "custom"
再加上:
[model_providers.custom]
base_url = "https://某个地址"
那基本就能锁定重点了。
3. 特别检查:这个 base_url 是不是你以前配置过的中转
这是我这次最想提醒大家的一点。
很多人会误以为:
“这个地址看起来好陌生,是不是 Codex 自动给我配上的?”
但更常见的情况其实是:
那是你自己以前配过的中转地址,只是后来忘了。
所以一定要回忆一下:
- 你以前有没有配过兼容 OpenAI 的中转?
- 有没有接过第三方 API 网关?
- 有没有为了别的工具改过这个配置文件?
- 有没有沿用旧机器、旧 dotfiles、旧模板?
4. 直接做一个对照实验
最简单粗暴的方法就是先备份配置,然后临时移走:
cp ~/.codex/config.toml ~/.codex/config.toml.bak
mv ~/.codex/config.toml ~/.codex/config.toml.off
codex
然后重新测试:
/status
hello
如果这样一来恢复正常,那就说明问题八九不离十就在原配置里。
5. 别忘了检查 alias、环境变量和启动参数
有时候不是配置文件本身的问题,而是 shell alias、启动脚本或者环境变量覆盖了配置。
可以顺手查一下:
type codex
which codex
alias | grep codex
env | grep -i codex
env | grep -i openai
避免你刚删完配置,结果另一个地方又把 provider 指回去了。
还有一个顺手提醒:排查配置时一定要注意密钥泄露
这次排查过程中,我还顺带踩到了另一个容易忽略的坑:
在贴配置文件求助时,很容易把 token、密钥一并发出去。
像下面这种字段,一定要先打码再发:
ANTHROPIC_AUTH_TOKEN = "..."
OPENAI_API_KEY = "..."
如果你已经把真实 token 发到了公开环境里,最稳妥的做法就是:
- 立刻撤销旧 token
- 重新生成新的 token
- 不要继续使用已经泄露的那一把
这个动作的重要性,不比修 CLI 低。
最后总结
这次故障排查,最后的结论其实很简单:
Codex CLI 断流,不一定是网络问题。
如果你本地残留了custom provider配置,尤其是以前用过的中转地址,就很可能在流式响应阶段踩坑。
我这次最终确认的根因是:
-
/status里显示了custom provider -
~/.codex/config.toml中存在:model_provider = "custom"- 自定义
base_url
-
这个
base_url不是 Codex 默认生成的,而是我之前自己配置过的中转站地址 -
删除这段配置后,Codex CLI 立即恢复正常
所以如果你也碰到类似的报错:
stream disconnected before completion
建议别只顾着换网络,先去看看自己的:
~/.codex/config.toml
很多时候,真正的问题就在那里。
附:我最终保留下来的简化配置
disable_response_storage = true
model = "gpt-5.3-codex"
model_reasoning_effort = "medium"
personality = "pragmatic"
[features]
multi_agent = true
[projects."/Applications"]
trust_level = "trusted"
[projects."/Users/zhangyusheng"]
trust_level = "untrusted"
[shell_environment_policy]
inherit = "core"
[shell_environment_policy.set]
CLAUDE_CODE_ATTRIBUTION_HEADER = "0"
CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS = "1"
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = "true"
ENABLE_TOOL_SEARCH = "false"