
用了挺长一段时间的 Codex最烦人的不是模型写错代码而是聊到一半网络抖了一下界面就卡在“重连中”等了快半小时还在转。后来翻日志才发现它根本不是在思考而是掉进了一个自动重试的循环里。这个问题的坑点在于罪魁祸首不是模型输出慢而是超时配置和重试策略叠在了一起。最后我就在配置里加了一行参数把整个等待时间从“半天”压到了几十秒。这篇文章就把排查链路和那一行配置完整记录下来也给同样被 Codex 重连折磨的朋友一个可以直接照抄的方案。1. 重连卡住的现象它看起来在思考其实在无限重试1.1 我遇到的现场还原当时我正在让 Codex 改一个跨模块的重构任务上下文已经很长了对话到后半段明显变慢。中间我切了一下电脑的网络从办公室有线网切到手机热点回来后 Codex 就卡住了。界面上的表现很典型不再输出文字也没有报错就剩下一个转圈的光标旁边写着类似“正在重新连接”的状态。鼠标点任何地方都没反应CtrlC 第一次按下去也没立刻退出来好像 UI 线程也被阻塞了。我的第一反应是模型响应太大需要继续等。于是我又等了 10 分钟还是没有变化。这时候才觉得不对劲检查了一下终端日志发现里面并不是“思考中”而是一遍又一遍的请求重试记录。印象里有一行报错信息大致是local switch failed while handling codex endpoint /responses这个错误的字面意思很容易被忽略本地网络状态切换失败导致某个请求没有正常发出。但真正麻烦的是Codex 对这个失败的响应是“重新发起同一个请求”而不是停下来告诉用户“当前连接异常”。于是整个会话就像死循环一样每隔一段时间重试一次每次重试都会积累更长的等待时间。1.2 日志在哪里怎么看很多人遇到这类问题第一反应是去搜索引擎查报错其实更高效的办法是先看本地日志。Codex 的日志一般会放在当前用户目录下的 Codex 配置/日志目录里例如 macOS 和 Linux 上常见路径是ls ~/.codex/log/如果版本不同也可以打开 Codex 的调试输出模式开启后会直接把请求链路打到终端codex --log-level debug开启日志以后你可以清楚地看到这样一条时间线请求发出目标端点识别为/responses这是模型生成文本的流式接口。一段时间后网络层出现异常错误里出现 local switch failed。客户端没有返回错误给用户而是标记为“可重试”。等待若干秒再次发出同一个请求。再次失败等待时间翻倍继续重试。实际上当你看到“重连中”的提示时它很可能就是在第 3 步和第 4 步之间反复横跳。搞清楚这个机制以后问题就从玄学变成了配置问题。1.3 它真的在“重连”吗从用户视角看“重连中”是一个让人安心的词像是客户端正在努力恢复连接。但从协议层看Codex CLI 在大多数情况下并没有断连它只是在上一次 TCP 连接上死等服务器响应。打个比方你打电话给客服电话其实已经通了但对面一直没人说话。你以为是对方在查资料就一直拿着电话等实际上线路远端早就挂了。这时候最理想的做法是果断挂断、重新拨号而不是举着电话听半小时电流声。Codex 的自动重试逻辑本来是为了解决临时网络抖动它的出发点是好的——如果只是丢一个包重试可以自动恢复但当底层网络路径发生变化、本地出口失效之后重试不但不会恢复反而会让同一个请求反复进入队列消耗大量时间。重点在于这不是服务器的锅而是客户端本地网络状态变了。2. 为什么“重试几次”会演变成“卡半天”超时时间和重试次数的乘法效应2.1 连接超时和请求超时是两回事要理解这个问题先要分清两个不同的超时概念概念作用默认倾向连接超时建立 TCP 连接的最长等待时间通常较短几秒到几十秒请求超时从发出请求到收到响应的最长等待时间通常很长适合模型推理场景Codex 这类 AI 编程工具比较特殊它需要请求远程模型接口而模型生成一段代码可能需要几十秒甚至更久。所以官方默认把请求超时设置得非常大这是合理的否则正常的模型推理也会被误杀。但如果你的本地网络已经出了问题这个“大超时”就会变成灾难。每次请求都会傻傻地等到超时上限才放弃而且放弃后并不是结束而是触发自动重试。关键公式在这里总等待时间 ≈ 单次请求超时时间 × 重试次数如果单次超时是 240 秒重试次数是 10 次理论上最坏情况就是 40 分钟。如果再叠加指数退避算法实际耗时会比这更长。这就是“卡半天”的数学来源不需要任何神秘原因。2.2 指数退避让等待时间越来越长很多网络客户端在重试时都会使用指数退避也就是第一次失败等 1 秒第二次等 2 秒第三次等 4 秒第四次等 8 秒……这样设计是为了避免对服务端造成冲击正常情况下很科学。但问题在于指数退避面对“本地网络长期异常”时会产生反效果。你看到的不是一次次快速失败而是间隔越来越长的重试视觉上就变成了“卡得越来越厉害”。我曾经观察过日志从最开始的 3 秒一次到后面变成几十秒一次整个终端没有任何输出变化只有日志文件在默默变大。更难受的是有些客户端还会加入随机抖动让退避时间在某些情况下显得更长。对于日志分析来说抖动也会让问题看起来毫无规律但实际上背后只有一套简单的策略失败后等一等再试。2.3 local switch failed 为什么比普通超时更麻烦按理说“请求超时”并不可怕到点失败就行。但 local switch failed 这种错误不一样它背后是网络路径切换也就是本地网络环境发生了结构性变化比如从有线网络切到无线网络从公司内网切到家用网络多网卡设备发生路由漂移系统休眠唤醒后底层网络栈重建在这种场景下客户端持有的旧连接已经失去意义原来的 TCP 连接可能还处于半开状态客户端以为连着实际服务器根本收不到数据。半开连接不会立刻触发错误要等多轮 TCP 超时才会暴露出来。此时无论重试多少次只要客户端没有重新走一遍“选路 建连”的过程请求就不可能成功。我不太想在这里把问题上升到网络工程层面但从实操角度来看这种错误一旦出现靠 Codex 自己重试大概率是等不到恢复的。正确姿势是人工中断或者通过配置把自动重试快速终止让错误直接暴露出来。3. 一行配置解决漫长等待具体操作和参数说明3.1 Codex 配置文件在哪里Codex 的全局配置文件通常位于用户目录下~/.codex/config.toml不同操作系统的路径会有点差异但思路相同。如果你不确定自己的配置文件在哪可以在终端里运行codex --help部分版本会在帮助信息里直接显示配置路径或者在启动日志里打印loaded config from ...。打开配置文件后你会发现里面主要是一些模型和接口相关的设置例如模型名称、接口地址、密钥读取方式等。我要改的就是和网络请求策略相关的参数。请注意不同版本可能存在字段名差异核心思路是把“超时时间调短”和“重试次数调小”这两个值明确写出来。3.2 实际操作增加这一行我以常见的配置写法为例在 config.toml 里增加一个请求超时配置request_timeout 90s如果你希望自动重试也不要太疯狂可以把重试次数也一并设置max_retries 2两个参数合起来就是本文标题里说的“一行配置搞定”。有些版本支持环境变量写法例如export CODEX_REQUEST_TIMEOUT90s export CODEX_MAX_RETRIES2效果等同放在 shell 配置里也可以。设置生效后请求如果在 90 秒内没有拿到响应Codex 不会继续安静等待而是直接在界面上给出错误把控制权还给你。这时候你可以手动检查网络、重新运行命令或者调整模型参数而不是像个闹钟一样反复重试。3.3 为什么“调短超时”反而更稳有人可能会担心把超时调短会不会让正常的模型输出也被打断这条担心有道理但需要结合使用场景来看。对于 Codex 这种对话型工具单次正常响应的等待时间通常在 15 到 30 秒之间。涉及大段代码生成或者长上下文理解时偶而会到 60 秒以上。所以我把超时设置在 90 秒既不会误杀正常响应又能快速识别异常状态。更大的收益在于配置调短以后Codex 失败时能及时给出直观报错你可以马上看到是网络问题还是模型问题而不是面对一个永远转圈的光标。这种“快速失败”的能力在调试场景下比“重试成功”更有价值——因为它把不确定性转变成确定性让你知道下一步该干什么。3.4 实测效果从等待半小时到几十秒出结果修改完配置后我重启了一次 Codex并且故意模拟了网络切换场景结果如下配置前请求陷入重试循环日志持续滚动界面约 25 分钟没有实际输出。配置后网络异常后约 90 秒界面直接显示连接错误不再进入循环我切回正常网络手动重新发起同一需求过程秒级完成。这里有一个很微妙的体验差别过去我害怕看到错误总觉得报错意味着“坏了”现在反而希望它快速报错因为知道错误是真的有方向可查。一句话总结调短超时不是为了让 Codex 更“急躁”而是让它不再用相思等一个永远等不到的回应。4. 另一种常见卡顿模型名不支持也会触发循环重试4.1 报错长什么样重连超时的问题解决以后我又撞上了另一个卡顿。某次启动 Codex 后界面同样好久不出内容打开调试日志看到一行疑似不支持的模型名提示大意是the gpt-5.6-sol model is not supported when using codex with a ...这行报错粗看像网络问题但它实际是配置层面的问题——你请求的模型名称在当前使用的接口服务里根本不存在。可能原因包括模型名写错比如多了一个后缀。在 Codex 配置中手动指定了某个模型但当前接口不支持。通过环境变量或者动态拼接方式生成了模型名结果拼接出了问题。这种报错的猫腻在于当模型不支持时部分客户端不会直接拒绝请求而是可能返回一个重试状态于是 Codex 又进入自动重试模式。你看到的依旧是“转圈等待”但背后的原因已经不是网络而是模型名。4.2 排查顺序先日志再配置最后看缓存我的经验是遇到 Codex 长时间没有任何输出时按下面顺序排查打开 debug 日志看请求是否真正发出。如果日志里出现/responses后长时间无响应优先怀疑网络超时或模型不兼容。如果日志里直接出现 model is not supported 这类文字就检查配置里的 model 字段。修改配置后完全退出 Codex 再启动不要用热重载因为配置缓存可能还保留旧值。还有一个高发问题用户明明改了配置文件但 Codex 启动时又被环境变量覆盖了设定值。常规优先级一般是“命令行参数 环境变量 配置文件”所以在怀疑配置不生效时也要顺手检查当前 shell 里有没有相关的环境变量残留env | grep -i codex把相关的变量临时清除后再跑一次往往就能看到效果。4.3 配置解析里容易被忽略的细节如果你手动编辑过 Codex 的配置文件还要留意 TOML 语法本身。看起来很小的错误比如漏掉引号、写成了中文字符的冒号、数组后面多了一个逗号都会导致整个文件解析失败。而解析失败后的表现并不总是“报错退出”有的版本会静默回退到默认配置让你误以为“改了没效果”。建议每次修改完配置后用简单的命令检查一下文件是否正常例如codex doctor或者干脆启动一个最简单的对话确认配置是否真的被加载。日常使用中我习惯保留一份原始的干净配置备份改动前先复制一份再在副本上滚动修改。这样即使改坏了也能秒级回滚不需要靠记忆恢复原来的内容。5. 从重连问题延伸开去让 Codex 更稳的几个日常习惯5.1 网络切换后不要硬等主动重启会话我现在已经养成了一个习惯只要电脑发生网络切换比如从 WiFi 换到热点、从公司网切到家里网我都会主动退出当前 Codex 会话再重新进入而不是让它在后台尝试重连。这听起来很粗暴但实际很有效率。因为长会话本身维护着大量的上下文状态一旦底层连接失效前面提到的半开连接问题就会让客户端卡住。主动重启等于把状态清空重新建立一条干净的请求链路。对于绝大多数任务来说断点重续的代价远小于“等它自动恢复”的代价。当然如果你正在运行的是一个不长不短的任务可以先复制一下当前对话的关键内容再重启。Codex 提供了历史记录和会话恢复能力但我在实际使用中仍然倾向于“关键信息自己留一份”特别是需要基于大上下文继续编辑时重新粘贴一段清晰的需求准确率会更高。5.2 用看门狗方式避免“隐形等待”除了在配置层面缩短超时我还会配合使用一个简单的“看门狗”命令在特定场景下兜底timeout 120 codex这里的核心思路是无论 Codex 内部如何重试外层运行时间超过 120 秒就直接终止。这个兜底看起来简单但对于自动化脚本或者长时间无人值守的任务来说非常有用。普通交互场景下不需要这样但如果你在跑批量任务或 CI 流程一个看门狗可以避免整个队列被一次卡死拖垮。用这种方式即使有一天配置没有生效也不会再出现“半天没有交作业”的情况。结合配置里的短超时内外两层防护一起作用Codex 的可用性会明显提升。5.3 启动时快速验证“三件套”我每次新装环境后都会先做一遍最简单的基础验证基本上可以筛掉大部分配置类问题确认网络环境正常能正常访问模型服务的接口。确认代码配置里的模型名和接口地址一致。确认认证信息能被当前终端读取且没有环境变量冲突。这三项只要没问题Codex 日常使用就不会出现那种“莫名其妙卡死”的状态。尤其认证信息这一项很多人忽略。举个例子当你配置了某个组织或项目分组时如果认证信息对应的组织权限不正确也会出现“无法加载组织设置”之类的状态而且界面表现同样是转圈等待。这类问题不是在网络层而是在身份信息层需要检查登录态和权限配置不能只靠改超时解决。5.4 我的体会快速失败比追求重连成功更实用回看这次排查对我启发最大的不是某条命令或某个参数而是“快速失败”这个工程思路。在没有调短超时之前我潜意识里总认为“没报错就是还有机会”于是放任客户端无限重试。但实际表明Codex 的自动重试在面对本地网络路径切换时几乎不能自愈反而会占用大量无效时间。相反的把超时缩短到合理范围让异常快速暴露在我面前反而给了我一次主动处理的机会。后来我把同样的思路用在了很多工具上比如自定义脚本、爬虫任务、数据同步流程凡是涉及网络请求的长任务我都会先确认“失败边界在哪里”。一个健康的长任务应该具备明确的两个能力做不完时知道什么时候停止以及失败之后能给出可读的错误提示。从这个角度看Codex 卡重连的那一天其实是一个很好的提醒——复杂工具越多越要给“等待”设置一个边界。现在我的 Codex 配置里那一行超时参数就一直留着了。它不是万能的比如真正的模型兼容问题、账号权限问题还是得靠日志去查但它确实把一个最磨人的“无限等待”变成了可控状态。如果你目前也正在被 Codex 的重连问题折磨我建议先别急着换网络或者重装环境打开日志看几十秒再改一下超时和重试次数大概率能直接从根源上把问题解开。