适用场景:Codex Desktop 一直开着,网络中断后已经恢复,浏览器也能正常上网,但 Codex 下一次发送消息时仍会依次重试 1/5 到 5/5,等待很久后才恢复。
这类问题看起来像“Codex 服务器不稳定”,但如果浏览器已经恢复、只有 Codex 固定重试五次,优先排查的通常不是普通断网,而是 代理切换后遗留的 WebSocket 路由或会话恢复问题。
本文整理一套在 macOS 上可回滚、风险较低的排查方法。核心原则只有三个:
- 先用日志确认是不是 WebSocket 五次重试;
- 先确认代理真实监听端口,绝对不要照抄别人的端口;
- 不修改
~/.codex/config.toml,只为 Codex 显式设置代理环境变量。
一、典型症状
高置信度的故障模式通常同时满足以下条件:
- Codex Desktop 在网络中断前后始终保持打开;
- 网络恢复后,浏览器和网页版 ChatGPT 可以正常访问;
- Codex 第一次发送新请求时固定经历五次重试;
- 日志出现
responses_websocket、Connection reset by peer或falling back to HTTP。
典型日志形态类似:
target=codex_api::endpoint::responses_websocket
failed to connect to websocket: Connection reset by peer
retrying ... 1/5 ... 5/5
falling back to HTTP这更像是代理后的 WebSocket 连接在网络变化后变“陈旧”了,并不等于登录失效,也不代表历史任务损坏。
二、先查日志,不要先清缓存
在终端执行:
rg -i "network_changed|responses_websocket|retry|falling back|connection reset" \
"$HOME/Library/Logs/com.openai.codex" "$HOME/.codex" 2>/dev/null如果没有安装 rg,可以改用系统自带的 grep:
grep -RinE "network_changed|responses_websocket|retry|falling back|connection reset" \
"$HOME/Library/Logs/com.openai.codex" "$HOME/.codex" 2>/dev/null日志位置和格式可能随 Codex 版本变化。这里只做只读搜索,不要编辑 SQLite 日志库或状态文件。
三、确认代理的真实监听端口
先查看 macOS 当前系统代理:
scutil --proxy再查看本机正在监听的 TCP 端口:
lsof -nP -iTCP -sTCP:LISTEN找到代理客户端对应的 HTTP 或 Mixed 端口后,再单独确认:
lsof -nP -iTCP:<实际端口> -sTCP:LISTEN例如某台机器可能使用 7892,但不同客户端、不同设备的端口并不一样。如果把一个没有监听的端口写进 Codex 配置,原本偶发的重连会直接变成完全无法连接。
还可以用一次代理请求验证链路:
curl -x http://127.0.0.1:<实际端口> -I https://chatgpt.com这里主要看请求能否稳定完成。即使返回 403,也可能说明代理到目标站点的网络链路是通的;不要只用业务状态码判断代理进程是否存活。
四、推荐修复:给 Codex 显式设置代理
我把安全修改流程做成了一个可复用技能:
安装或下载技能后,进入技能目录,先查看当前状态:
python3 scripts/configure_proxy_env.py status确认端口确实在监听后,再执行:
python3 scripts/configure_proxy_env.py apply \
--proxy-url http://127.0.0.1:<实际端口>脚本只管理 ~/.codex/.env 中的三个变量:
HTTP_PROXY=http://127.0.0.1:<实际端口>
HTTPS_PROXY=http://127.0.0.1:<实际端口>
NO_PROXY=localhost,127.0.0.1,::1它会保留 .env 里的其他键、在变更前创建带时间戳的备份,并使用原子写入;不会修改 config.toml。如果代理 URL 含有账号密码,状态输出也会隐藏凭据。
在出现问题的环境中,wss:// 请求会沿 HTTPS 代理解析,因此显式设置 HTTPS_PROXY 可以让 WebSocket 在网络恢复后重新走确定的代理路径。这里的目标是让 WebSocket 稳定重连,不是强制 Codex 改用 HTTP。
五、完全退出并重新打开 Codex
仅关闭窗口通常不够。请完全退出 Codex,再重新打开,因为 Codex 需要在新进程启动时读取 ~/.codex/.env。
重启后依次验证:
- 原有任务列表仍然可见;
- 正常发送一条请求,可以快速得到响应;
- 临时断开网络再恢复;
- 再发送一条请求,不再经历固定的五次重试;
- 日志不再出现本次测试对应的 WebSocket 五连重试。
为了观察代理是否稳定,可以做短时间连续探测:
for i in {1..10}; do
date '+%H:%M:%S'
curl -x http://127.0.0.1:<实际端口> \
-sS -o /dev/null -w 'status=%{http_code} time=%{time_total}\n' \
https://chatgpt.com
sleep 3
done重点看是否频繁连接失败、超时或耗时突然暴涨,而不是要求所有状态码都必须是 200。
六、如何回滚
执行 apply 时,脚本会打印备份文件路径。需要回滚时运行:
python3 scripts/configure_proxy_env.py restore \
--backup ~/.codex/.env.bak-YYYYMMDD-HHMMSS-ffffff如果原本没有 .env,只想移除脚本管理的三个键:
python3 scripts/configure_proxy_env.py remove回滚后同样要完全退出并重新打开 Codex。
七、不建议优先尝试的方法
1. 为了关闭 WebSocket 新建自定义 Provider
有些方案会在 config.toml 中创建第二个 Provider,并设置类似 supports_websockets = false 的参数。它可能改变传输行为,但 Codex Desktop 可能按 Provider 身份区分或过滤任务,造成“历史任务不见了”的错觉。
为了保留登录状态和任务历史,不要把它作为首选修复。
2. 盲目添加 CODEX_*WEBSOCKET=false
这类环境变量可能因版本变化而无效,部分 Codex 构建还会过滤从 .env 载入的 CODEX_ 前缀变量。没有针对当前版本验证前,不要把它当作通用开关。
3. 反复清理缓存
如果根因是代理进程、系统代理或长连接恢复,清缓存通常不会解决问题,还可能增加历史状态风险。先查日志、监听端口和代理稳定性,信号更直接。
八、显式代理仍然无效怎么办
继续按以下顺序排查:
- 是否同时运行了多个旧代理进程;
- VPN/TUN 模式是否在网络恢复后卡住;
- 系统代理是否仍指向旧端口;
- 防火墙或安全软件是否拦截 WebSocket;
- 公司网络是否做 TLS 检查;
- DNS 是否在切网后短暂返回错误结果;
- 当前代理节点是否支持稳定的 WebSocket 长连接。
如果普通网页正常、连续代理探测也稳定,但只有 Codex 仍五次重试,就保留相关日志并反馈给 Codex 支持渠道。本文方案来自特定 macOS、代理客户端和 Codex 构建上的实测,不应被理解为所有版本的官方通用配置。
总结
“浏览器已经恢复,但 Codex 仍重连五次”通常不是简单的断网,而是代理环境下的 WebSocket 路由恢复问题。最稳妥的处理顺序是:
- 从日志确认
responses_websocket五连重试; - 用
scutil和lsof找到真实代理端口; - 通过
~/.codex/.env显式设置 HTTP/HTTPS 代理; - 完全重启 Codex;
- 做一次断网恢复测试,并保留可回滚备份。
不要猜端口,不要先改 Provider,也不要在没有验证的情况下照搬禁用 WebSocket 的偏方。