Codex 报 Stream disconnected before completion,或者弹出 Unexpected status 401 Unauthorized,第一件事不是重装客户端,而是打开 config.toml 看 base_url。用 TaoToken 的兼容通道时,这两类报错往往出在同一处:地址写得不对,或者身份根本没通过。去 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 创建一把 Key,把 Codex 的 Base URL 填成 https://taotoken.net/api —— 注意末尾不要补 /v1,这一点和常见示例刚好相反。
很多人看到报错就开始怀疑模型、怀疑额度、怀疑网络运营商,甚至把 Codex 整个卸了重装。实际上,流式连接断开和身份认证失败这两个错,排查路径非常短:先看地址,再看 Key,最后才看网络。下面按报错出现的先后顺序,把 Codex App、Codex CLI、VS Code 里的 Codex 插件统一捋一遍,遇到别的 4xx/5xx 错误码也能对照着定位。
1. Stream disconnected before completion:先把地址和链路分开看
1.1 这条报错到底在说什么
Stream disconnected 不是 Codex 崩了,而是流式响应中途被掐断。Codex 用 SSE 拿回复,正常结束时服务端会推一个 response.completed 事件;如果连接在事件到达之前被关闭,客户端就只能报:
Stream disconnected before completion: stream closed before response.completed另一种带 URL 的长这样:
Stream disconnected before completion: error sending request for url (https://.../responses)注意第二种,它把请求地址也打出来了。这个 URL 本身就是线索:路径对不对、有没有多一层、是不是混进了别的服务商域名,一眼就能看出来。
这里有个容易忽略的点:鉴权失败经常不会老老实实返回 401。有些服务端会先把流建起来,发现 Key 不对再掐断,客户端收到的是"流断了",而不是"未授权"。所以 Stream disconnected 和 401 经常是同一个病的两种表现,别把它们当两个独立问题查。
1.2 base_url 该不该带 /v1,这是本篇最关键的一行
市面上大量教程都会写:base_url 要以 /v1 结尾,不带就是错的。这个结论在部分服务上成立,但不能无脑套到 TaoToken 上。
走 TaoToken 兼容通道时,填进 Codex 的地址是:
https://taotoken.net/api末尾不带 /v1。多补一层,客户端拼出来的路径就多一段,服务端认不出这个路由,连接刚建立就被关掉,表现就是 Stream disconnected。
| 写法 | 结果 |
|---|---|
https://taotoken.net/api | 正确,兼容通道的 Base URL |
https://taotoken.net/api/v1 | 路径多一层,容易直接断流 |
https://taotoken.net | 少了路径,连不上接口 |
| 别家服务商地址 | 域名对不上,钥匙插错了锁 |
判断标准只有一条:Base URL 只认服务方给的那一串原文。拿不准就回 模型广场 对照当前的接入说明,不要凭印象补后缀。
1.3 用一条 curl 把网络层和配置层分开
怀疑网络之前,先确认是不是配置写错了。反过来,怀疑配置之前,也要确认链路本身通不通。做法很简单,只测连通性,不测业务:
curl -sS -o /dev/null -w "%{http_code}\n" https://taotoken.net/api只要它能打印出一个 HTTP 状态码——哪怕是 404、405——就说明 DNS 解析、TLS 握手、出口链路都是通的,问题在 Codex 的配置里。如果卡住不动,或者报Could not resolve host、Connection timed out,那就是链路层的事,这时候改 base_url 改到天亮也没用。
还有一种更省事的隔离方式:去模型对话页面用同一把 Key 发一条"你好"。如果网页里能正常出字,说明 Key 和模型 ID 没问题,锅在 Codex 本地配置;如果网页里也报错,那就先解决 Key 或额度的问题,别在 config.toml 上耗时间。
2. 401 Unauthorized:auth.json 和 config.toml 要同时对
2.1 auth.json 里那把 OPENAI_API_KEY
地址配对了还报 401,重点就落在认证文件上。Codex 的认证信息一般在用户目录下的.codex/auth.json:
{ "OPENAI_API_KEY": "YOUR_API_KEY" }这个文件里踩坑最多的地方,按出现频率排大概是这几种:
- Key 复制时带上了首尾空格或换行,JSON 解析不报错,服务端校验直接失败。
- 手动给 Key 加前缀。能不能加、该不该加,以控制台给出的原文为准,原样粘贴最稳。
- Key 已经被删掉或重新生成过,本地还留着旧的那把。
- 之前登录过官方账号,auth.json 里残留了额外的登录字段,和手填的 Key 混在一起,客户端不知道以哪个为准。
一把干净的 Key 从 TaoToken 控制台创建,创建完当场复制,别在聊天记录里翻旧的。如果之前登录过官方账号,建议把 auth.json 里多余字段清掉,只保留当前使用方式需要的那一项。
2.2 model_provider 和 provider 定义的 ID 必须一致
401 还有一类很隐蔽的成因:配置结构写错了区块。比如把 model_provider 写进了 sandbox 相关的表里,Codex 读不到它,最后拿着一个空 provider 去请求,自然是被拒。下面是可复制的正确结构:
model = "YOUR_MODEL_ID" model_provider = "taotoken" [model_providers.taotoken] name = "TaoToken" base_url = "https://taotoken.net/api" wire_api = "responses" requires_openai_auth = true两个要点:model_provider的值必须等于[model_providers.xxx]里的那个xxx,大小写也要对得上;model别自己猜,模型名以官网模型广场当时列出的 ID 为准。wire_api这一项按你所用通道的类型填,接入文档写什么就填什么,不要照抄别人的截图。
2.3 改完必须完全退出,而不是关掉窗口
这条被无数人忽略。改完 config.toml 和 auth.json 之后直接继续用,客户端根本没重新读配置,当然还是报 401。
正确做法是:把 Codex App、Codex CLI、编辑器里的 Codex 进程全部退出——关窗口不算退出,托盘图标还在跑;然后用一个全新的会话发一条最短的 prompt 测试。CLI 场景下,长期开着的终端会话也可能缓存了环境变量,开一个新终端更保险。
3. 403、429、499:这些不是配置写错了
3.1 403:额度和熔断
403 的两种典型文案,一种是余额和订阅额度不足,另一种是 API Key 熔断已开启。前者是账上的事,后者是风控的事:短时间内连续失败请求攒够了,Key 会被临时冻结一段时间。
这两种都跟 config.toml 没关系,改配置只会浪费时间。打开 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 进控制台,看这把 Key 的用量、有效期和当前状态,该充值充值,该换 Key 换 Key,该等就等。顺手把失效的那把旧 Key 删掉,免得下次又复制错。
3.2 429:多开 Session 的代价
429 的原文通常是exceeded retry limit, last status: 429 Too Many Requests。它只有一个意思:单位时间内请求太多。Codex 特别容易触发,因为它天生适合并行——一个窗口跑重构,一个窗口跑测试,再加两个 Agent 同时开,请求量瞬间翻几倍。
处理办法是降并发,不是重试。原来同时跑十个 Session,先压到两三个,观察是否恢复。如果降下来就好了,结论很清楚:这是频率或并发限制,不是你配置有问题。反复重试只会让熔断来得更快。
3.3 499:客户端自己关的门
499 的含义比较特殊,它表示客户端主动断开了请求。两种情况:一是你手动点了 Stop 或关了 Session,这种完全不用管;二是没人操作却频繁出现,同时 Codex 长时间卡在 Waiting 或 Processing。
前者属于正常现象,后者才需要查。可以中断当前任务重发、新建一个 Session、顺手看一眼网络是否抖动。偶发一次不用处理,持续出现再往下查链路和上游状态。
4. 400、502、503:参数、网关与上游服务
4.1 400 多数出在思维等级和请求内容
400 是请求参数不合法,和 Key 是否有效关系不大。最常见的一种是思维等级不被支持,比如配置里写了:
model_reasoning_effort = "xhigh"而当前模型并不支持这个档位,服务端就会返回 bad_request。解决办法是查这个模型支持的 Reasoning Effort 档位,改成范围内的值,别硬填最高档。
另一种是请求内容触发了内容策略。这类错误反复重发一模一样的 prompt 是没用的,改措辞、换一个测试输入、先跑一条无关的短句确认链路正常,再逐步加回原内容。
4.2 502 和 503:先重试,再换 Session,最后才怀疑配置
502 的典型文案里会带一个 request ID,或者出现FAKE_200_JSON_ERROR_MESSAGE_NON_EMPTY: stream_read_error这类看着很吓人的字样。它的本质是网关没能从上游拿到有效响应,多数情况下带点随机性,重发一次就好,连续失败就新建 Session 再试。
503 的文案一般是"当前模型所有渠道不可提供"或"服务暂时不可用"。按顺序查三件事:模型 ID 是否写错、该模型当前是否可用、是不是正在维护。模型名一定要从模型广场抄,别拿小写、大写、带连字符的变体去试探,试探出来的 503 只会让你误判。
5. 一张对照表加一条排查顺序
5.1 错误码对照表
| 报错 | 含义 | 先查什么 |
|---|---|---|
| Stream disconnected | 流式连接中断 | base_url 是否多带 /v1、链路是否通 |
| 400 | 请求参数不合法 | 思维等级、请求内容 |
| 401 | 身份认证失败 | auth.json 的 Key、provider 结构 |
| 403 | 额度不足或 Key 熔断 | 控制台用量与 Key 状态 |
| 429 | 请求频率超限 | Session 并发数 |
| 499 | 客户端主动关闭 | 是否手动停止、是否超时 |
| 502 | 网关拿不到上游响应 | 重试、换 Session |
| 503 | 服务暂时不可用 | 模型 ID、渠道可用性 |
5.2 按这个顺序查,别一上来改十条配置
第一步看报错类型,是具体状态码还是 Stream disconnected;第二步只看 base_url,确认写法没多没少;第三步看 API Key,确认 auth.json 内容干净;第四步看模型 ID,确认它真实存在;第五步看 model_provider 和 provider 定义的 ID 是否一致;第六步在 429 时降并发;第七步用 curl 或网页端独立测一次,判断锅在客户端还是链路;第八步再去考虑服务端临时异常。
顺序很重要。同时改五处配置,改好了你不知道是哪一处起的作用,改坏了下一次更没头绪。
6. 排干净之后,回控制台确认这次调用记上了
配置保存、客户端完全重启之后,先用同一把 Key 在 TaoToken 模型对话 里发一条短消息,确认模型 ID 和 Base URL 都没填错。这一步能把"Key 问题"和"Codex 本地问题"彻底分开。
如果你打算长期用 Codex 写代码,可以顺手看一眼 Coding Plan 的套餐是否够用;需要新 Key 或者想让不同项目用不同 Key,在 控制台 API Keys 里创建和管理;如果同一把 Key 还要接 Claude Code,环境变量怎么写可以直接对照 Claude Code 接入文档。
我的习惯是每次改完配置,先去控制台看这次请求有没有被记上一笔。有记录,说明链路从客户端到服务端是通的,剩下出问题只会是本地环境;没记录,说明请求压根没发出去,那就回头看 base_url 是不是又被谁补了个 /v1。这个动作花不了三十秒,比对着报错瞎猜快得多。