Codex CLI 一直报 `stream disconnected`,我最后是这样定位到问题的

6 阅读10分钟

文章来源: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

这种错误最容易让人联想到几种原因:

  1. 本地网络不稳定
  2. 账号被限流
  3. 服务端暂时波动
  4. 客户端版本有问题

我最开始也是这么想的。
但继续观察后,我发现几个地方不太对:

  • 我已经换了很多次网络,问题没有任何改善
  • /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 默认自动生成的

这部分我觉得特别值得单独说一下,因为它很容易让人误解。

看到 custombase_url 的时候,很多人第一反应可能是:

难道 Codex CLI 默认安装完就会帮我配置一个第三方 provider?

我一开始也有点往这个方向怀疑。
但后来确认后发现,并不是。

真正的情况是:

这个 custombase_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 正常
  • 状态正常
  • 请求发得出去
  • 但在返回途中断掉

那就要优先怀疑下面这些方向,而不是只盯着网络:

  1. 自定义 provider
  2. 第三方中转层
  3. 协议兼容问题
  4. 流式响应实现问题
  5. 本地残留配置

这个思路上的转弯,其实比具体命令还重要。


如果你也遇到了类似问题,可以按这个顺序查

这里我把这次排查过程,整理成一个更通用的思路。

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 发到了公开环境里,最稳妥的做法就是:

  1. 立刻撤销旧 token
  2. 重新生成新的 token
  3. 不要继续使用已经泄露的那一把

这个动作的重要性,不比修 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"