前阵子在一台 Windows 工作站上同时维护 Codex CLI 和 OpenClaw,说实话把该踩的坑一口气踩了个遍。先是 OpenClaw 一启动就报 Codex CLI 找不到,费了半天劲把二进制路径修好,紧接着又冒出来网关层的 local proxy failed,最后模型链路也断了一截。整个过程下来,几乎把 Windows 环境下 CLI 工具、Node 运行时、本地网关、模型接入这几层问题全部过了一遍。这篇不是讲什么宏大架构,就是我在真实机器上的连环排障记录,围绕 CLI 启动失败、网关转异常、模型恢复三条主线展开,适合正在 Windows 上同时折腾 Codex CLI 和 OpenClaw 这类自动化框架的朋友照着排查。
1. 故障全景:环境、现象与排障主线
1.1 故障现场与技术栈交代
先说环境。机器是 Windows 11 Pro 24H2,PowerShell 7 作为主力终端,Node.js 20 LTS 通过官方安装包装的,WSL2 已启用但平时用得不多,主要跑在 Windows 原生这一侧。Codex CLI 是通过 npm 全局安装的,OpenClaw 同样是 npm 全局包。两者的关系需要先理清楚:Codex CLI 是一个交互式编码助手命令行工具,负责和模型服务通信、生成补全建议;OpenClaw 则是一个偏 agent 编排的框架,它会在 Windows 上调用外部 CLI 工具、编排任务流,也会自己发起模型请求。简单说,OpenClaw 经常会拉起 Codex CLI 去执行代码类任务,所以 Codex 的二进制能不能被找到、能不能被正确执行,直接决定 OpenClaw 能否跑通上游流程。
故障现场可以整理成一张清单,后面对照排查会很清楚:
| 现象 | 直接报错 | 初步判断 |
|---|---|---|
| OpenClaw 启动后任务失败 | 找不到 Codex CLI,任务在初始化阶段就中断 | CLI 二进制未被正确暴露 |
| 单独执行 codex 命令失败 | unable to locate the codex cli binary or required runtime components | 安装损坏或 PATH 缺失 |
| 网关请求转发异常 | cc switch local proxy failed while handling codex endpoint /responses | 本地转发进程或端口异常 |
| 模型返回异常/工具调用失败 | OpenClaw 侧收到空响应或超时 | 网关到上游模型的链路断裂 |
最初我怀疑是 OpenClaw 配置没写对,后来发现根本不是那么回事。当 OpenClaw 无法安全启动 Codex CLI 时,它会直接把初始化阶段标记为 failed,任务根本走不到模型调用那一步。而等我把 Codex 修好之后,真正的坑才露出来:网关层的本地转发服务根本没在监听,导致所有指向 Codex 的模型请求全部撞在空端口上。
1.2 一条排障链拆成三截
这类连环故障最怕眉毛胡子一把抓。我的习惯是用“链路分层法”把问题切成段:第一段是 CLI 工具层,解决“命令能不能跑起来”的问题;第二段是网关层,解决“请求能不能被正确转发”的问题;第三段是模型层,解决“上游模型响应能不能回来”的问题。每一段都有独立的验证手段,段与段之间用最小请求去串。这样定位问题位置很快,不会因为上游的日志刷屏而下错结论。
这次的三段正好对应标题里的核心词:CLI 启动失败对应工具层,网关对应转发层,模型恢复对应上游层。下面的章节按照实际排查顺序展开,每一段都会给出具体的命令、判断依据和修复动作。
2. 第一关:Codex CLI 启动失败与二进制定位修复
2.1 “unable to locate the codex cli binary” 是怎么来的
Codex CLI 在 Windows 上有两种常见安装形态:一是官方提供的安装包,直接生成独立的 .exe;二是通过 npm 全局安装,npm 会生成一个 .cmd shim 和一个指向实际入口脚本的软链结构。我这台机器用的是 npm 方案,所以最终解析路径是这样的:输入codex命令后,PowerShell 会在 PATH 中查找 codex.cmd,codex.cmd 再调用 node.exe 去执行真实入口文件。任何一个环节断裂,都会直接报出那句经典的unable to locate the codex cli binary or required runtime components。
这句话的字面意思是“找不到 codex cli 二进制文件或所需的运行时组件”。在 npm 安装模式下,它通常由以下几种原因触发:
- PATH 中没有包含 npm 全局 bin 目录,比如
%APPDATA%\npm,导致 shim 根本找不到。 - npm 全局包安装不完整,入口文件缺失或者 node_modules 里的依赖被清掉了一部分。
- Node.js 版本和 Codex CLI 要求的运行时版本不匹配,常见的表现是入口脚本启动后抛异常,但外层 shim 只给出了一个笼统的错误。
- 杀毒软件或安全策略把 node.exe 或 Codex CLI 的某个运行时组件锁定,启动直接被拦截。
- 多用户环境下 npm 全局目录被权限限制,安装路径看起来在,但实际没有读权限。
Windows 上还有一个隐蔽问题:很多教程是 Linux/macOS 思路,让你用which codex、export PATH,到了 PowerShell 里这套完全不成立。我先用Get-Command codex看了下 shim 解析结果,再用where.exe codex确认系统搜索路径,发现 Command 返回的路径指向一个根本不存在的目录——这就是安装残留加 PATH 混乱叠加出来的结果。
2.2 修复二进制定位的实操路径
修复过程按顺序分四步,每步都有独立验证,不会做了前面忘了后面:
第一步,确认 Node.js 和 npm 本身没问题。在 PowerShell 里跑:
node -v npm -v npm config get prefixnpm config get prefix输出的是全局安装目录,npm 的全局 bin 目录通常是%prefix%下的node_modules\.bin,而 Windows 安装 Node 时更常见的全局 bin 是%APPDATA%\npm。手动安装版和安装包版的位置不一样,这一步必须看清楚。我这里的 prefix 是C:\Users\me\AppData\Roaming\npm,方向对了。
第二步,检查当前会话的 PATH 是否包含这个目录:
$env:PATH -split ';' | Where-Object { $_ -match 'npm' }如果没有命中,直接加入用户级环境变量。推荐用setx而不是临时修改$env:PATH,因为临时变量只对当前窗口有效,重启后又得重新来一遍。不过setx有个副作用:它会截断超过 1024 字符的变量,所以如果你 PATH 已经很长,优先用系统设置的 GUI 面板去编辑。
第三步,重装全局包。这一步可以把损坏的入口文件、运行时组件全部重新拉一遍:
npm uninstall -g @openai/codex npm cache clean --force npm install -g @openai/codex需要说明的是,不同版本包名可能有差异,有的版本还是codex,有的换成了@openai/codex,重装前用npm list -g --depth=0看一下实际包名,不要凭记忆硬来。
第四步,用几个命令验证是否真正修复:
codex --version codex --help codex exec "print('hello')"如果codex --version正常输出、codex exec也能跑完,说明 CLI 层已经通了。顺带一提,如果你在 PowerShell 里执行脚本文件仍然闪退,大概率是执行策略问题,可以先跑一下Get-ExecutionPolicy确认状态,再用Set-ExecutionPolicy -Scope CurrentUser RemoteSigned放宽策略,这不是 Codex 的问题而是 PowerShell 脚本策略的问题。
3. 第二关:网关报错与 local proxy 恢复
3.1 网关在 Codex + OpenClaw 链路中的真实角色
CLI 修好后,我以为马上就能用了,结果 OpenClaw 一跑任务,新的报错立刻出来:cc switch local proxy failed while handling codex endpoint /responses。先搞清楚网关在这条链路里干什么。
我在这台机器上搭了一个本地网关服务,端口固定在 8787。所有需要走模型的地方——Codex CLI、OpenClaw 内部请求——都把base_url指到http://127.0.0.1:8787/v1,网关负责接收请求、做模型路由,最终转发到真正的上游,也就是 DeepSeek API 或本地 Ollama 上的 Qwen2.5-3B。好处是:换模型不用改每个工具的配置,只改网关的路由规则就行。风险是:网关挂了,整条链路全都断。这次的报错就是典型代表。
再解释cc switch。这是一个用来切换 Codex 后端 provider 的配置工具/脚本,它会修改 Codex 的配置或者环境变量,让 Codex 指向不同的模型服务。切换到local proxy模式时,cc switch预期本地网关服务已经在指定端口监听。如果网关进程没起来、端口被占用、或者配置里的 base_url 和实际监听地址不一致,就会抛出local proxy failed这个错误。
codex endpoint /responses则需要单独说明。Codex 默认走的是 OpenAI 的 Responses API 路径,也就是POST /responses,而不是更旧的POST /v1/chat/completions。很多自建兼容层只实现了聊天补全接口,没有实现/responses,导致 Codex 即使连上了网关,一发起请求就收到 404 或 501。所以网关必须能够识别并转发/responses这个路由,这也是“while handling codex endpoint /responses”这一串字眼的核心含义。
3.2 拆解 “cc switch local proxy failed while handling codex endpoint /responses”
这个报错可以逐段拆开看:cc switch local proxy failed说明cc switch在准备本地代理模式时失败了;while handling codex endpoint /responses说明失败发生在网关尝试处理 Codex 发来的/responses请求过程中。翻译成人话就是:本地这个转发服务没有正常工作,或者请求到了但转不出去。
我在实际排查中遇到的情况是端口被占用。8787 端口之前被一个 Java 进程占着,网关进程启动时端口绑定失败,但外层cc switch不会立刻检查端口状态,只是把 Codex 的配置改写成了http://127.0.0.1:8787/v1。于是 Codex 发起请求时,请求到了 Java 进程那里,返回的根本不是预期响应,网关层判定为 local proxy failed。Windows 下查端口占用我的习惯是走三步:
netstat -ano | findstr :8787这条命令输出 PID 和占用状态,当STATE显示LISTENING表示端口有进程在听。接着用:
tasklist /FI "PID eq 1234"确认这个 PID 是什么进程。如果确实是无关进程占用了端口,我不建议直接taskkill /F结束别人——先想一下这个端口是不是有别的用途,比如之前某个服务残留、管理代理、内网工具等。确认没用了再结束,或者换一个端口更稳妥。
除了端口被占,还有三类常见原因:
- 配置协议不匹配:环境中某个配置文件里写的是
https://,但本地网关是纯 HTTP,导致 TLS 握手失败。 - 上游模型配置为空或错误:网关转发到上游时,
api_key没填、模型名写错、上游服务地址拼错,网关只能把错误原样抛回给 Codex。 - 网关依赖的 Node 子进程崩溃:这个在 Windows 上比较隐蔽,网关进程本身可能还活着,但它拉起的工作子进程因为文件路径分隔符或权限问题崩了,主进程没有及时重启。
3.3 网关恢复完整检查单
我整理了一套检查顺序,现在每次遇到网关问题都按这个来,基本能在五分钟内定位:
- 检查本地监听是否存在:
Test-NetConnection -ComputerName 127.0.0.1 -Port 8787返回TcpTestSucceeded : True说明端口在听。如果 False,直接跳到第 3 步看进程是否存活。
- 向网关发一个最小请求验证基本路由能力:
curl.exe -X POST http://127.0.0.1:8787/v1/responses -H "Content-Type: application/json" -d '{"model":"test","input":"ping"}'如果立刻返回connection refused,说明网关根本没起来。这时候去启动网关进程,并观察启动日志里有没有端口冲突记录。
核对 gen 配置里的 base_url 和实际监听地址是否一致。注意区分环境变量和配置文件两个来源:环境变量优先级通常更高,Codex 里常见的
CODEX_BASE_URL、OPENAI_BASE_URL,OpenClaw 里常见的OPENAI_BASE_URL,全局搜一遍,别遗漏。这次的坑就是在.env文件里写了一个https://gateway.internal,但本地网关实际上监听的是http://127.0.0.1:8787,一层 TLS 的差异导致所有请求直接握手失败。检查上游模型服务的可达性。如果网关本身健康,但上游模型断了,表现为网关日志里出现 upstream timeout 或 connection refused。我在这一步用了一个简单到离谱的办法:直接用
curl.exe请求上游的 health endpoint,确认上游还活着,再回头查网关路由表。把日志级别调成 debug/verbose。Codex CLI 可以通过
--verbose或者环境变量开启详细日志,OpenClaw 也有日志等级配置,网关通常也支持DEBUG=*这类 Node 环境的调试变量。日志里的最后几百行往往已经写明了失败原因,比对着报错猜快太多。
网关层恢复后,我重新跑了一下codex exec,历史上第一次顺利通过了这个曾经失败的场景。但 OpenClaw 的任务仍然有一个环节没有完全恢复,原因出在模型链路上。
4. 第三关:模型链路恢复与 OpenClaw 端到端验证
4.1 一次请求从 CLI 到模型的完整流转
把链路完整串一遍,方便理解为什么模型链路还会再来一次故障。OpenClaw 在 Windows 上启动后,如果任务里包含代码能力,会尝试调用 Codex CLI,Codex CLI 收到指令后构造模型请求,请求发送目的地是 base_url 指定的网关地址。网关收到后,根据路由配置把请求转发到上游。上游可能是云端的官方模型接口,也可能是我本地 Ollama 上跑的 Qwen2.5-3B。
画出来就是:OpenClaw → 调用 Codex CLI 子进程 → HTTP 请求到本地网关 → 网关路由转发 → 上游模型服务 → 响应原路返回。
Windows 环境下这个链路最容易出的问题不是每个节点本身,而是节点之间的接口不匹配。比如 Codex 发的是/responses请求,但 Ollama 的 OpenAI 兼容层走的还是 chat completions 风格接口,如果网关不做转换,两边就接不上。这也是网关存在的意义之一,而不是简单做个 IP 转发。
4.2 模型接入配置参考:DeepSeek 与 Qwen2.5-3B
链路恢复后,我顺手把两个模型都接好了。DeepSeek 走的是云端 API,Qwen2.5-3B 走的是本地 Ollama。分别在两个层面配置:
Codex 侧的config.toml,一般位于~/.codex/config.toml,针对不同 provider 做区分。参考写法如下:
model_provider = "deepseek" [model_providers.deepseek] name = "DeepSeek" base_url = "http://127.0.0.1:8787/v1" env_key = "DEEPSEEK_API_KEY" wire_api = "responses"如果你想让 Codex 直接走本地 Qwen2.5-3B 的兼容接口,可以改成:
model_provider = "qwen-local" [model_providers.qwen-local] name = "Qwen2.5-3B" base_url = "http://127.0.0.1:11434/v1" wire_api = "chat"这里wire_api字段非常关键,它决定 Codex 用什么样的接口协议去和上游通信。如果上游是 Ollama,chat更稳;如果上游是网关且网关实现了/responses转换,用responses也完全没问题。
OpenClaw 侧则通过环境变量指定模型 provider 和 API 地址,常见做法是在启动前设置:
$env:OPENAI_BASE_URL = "http://127.0.0.1:8787/v1" $env:MODEL_PROVIDER = "openai" $env:MODEL = "qwen2.5-3b"本地模型如果用 Ollama,需要先确保 Ollama 正在运行并且模型已经拉取:
ollama pull qwen2.5:3b ollama serve这里有一个实测感受:3B 参数级别的本地模型在普通消费级机器上跑简单指令完全够用,但遇到长上下文或多步工具调用,响应延迟明显上升,OpenClaw 默认超时时间如果设置得太短,很容易把正常请求误判为失败。建议把 OpenClaw 调用的超时时间设置得比 CLI 单测场景更长,或者针对本地模型单独加长超时窗口。
4.3 端到端回归验证与 WSL2 提示处理
模型配置完成之后,我做了三层回归验证,确保不是“看起来好了但实际一跑就挂”的状态。
第一层,Codex CLI 独立验证:
codex exec "用一句话介绍你自己"能正常返回文本就说明 CLI 到网关到上游的链路是通的。
第二层,OpenClaw 最小任务验证,让 OpenClaw 执行一个不依赖外部工具、只依赖模型能力的简单指令。这一步如果通过,说明框架能正确拉起 Codex 并能拿到模型结果。
第三层,完整任务验证,加入文件读取、代码生成等工具调用。走到这一步才算整条链路真正恢复。
回归时还遇到过一个独立报错,值得单独写一下:OpenClaw 启动时提示“无法安全验证 WSL2 环境,请在 PowerShell 中运行 wsl -- status”。这个报错本质上是 OpenClaw 启动时会检测 WSL2 环境是否可用,用来决定某些沙箱或执行策略。处理起来也简单:在 PowerShell 里执行:
wsl --status wsl --update如果机器上确实不用 WSL,可以检查 OpenClaw 配置里有没有对应检测开关,把它关掉即可。这一步不影响本次故障链路,但如果在 Windows 上部署 OpenClaw 而不处理,它会一直干扰启动流程。
5. 同类故障速查表与 Windows 排障习惯
5.1 高频报错速查表
这次排障踩过的和顺带验证过的报错,整理成了表格,可以直接当速查用:
| 报错关键字 | 可能原因 | 优先排查动作 |
|---|---|---|
| unable to locate the codex cli binary or required runtime components | npm 全局路径不在 PATH、安装损坏、Node 版本不匹配 | 检查Get-Command codex、重装全局包、确认 Node 版本 |
| cc switch local proxy failed while handling codex endpoint /responses | 本地网关未监听、端口被占、base_url 不匹配 | netstat -ano查端口、核对 base_url、重启网关 |
| connection refused | 目标服务没起来或地址错误 | 分节点 curl 探测,确定断在哪一段 |
| upstream timeout | 上游模型响应慢、网关超时配置过短 | 加长超时、确认上游空闲状态 |
| 无法安全验证 WSL2 环境 | WSL2 未启用或状态异常 | wsl --status、wsl --update,或关闭检测 |
| 请求返回 404 / 501 | 接口不兼容,如上游不支持/responses | 检查网关路由转换,或改用wire_api = "chat" |
| 脚本闪退 | 执行策略限制、脚本入口缺失 | Get-ExecutionPolicy,用& .\script.ps1前台执行看错误 |
这几类问题在 Windows 环境下几乎属于“必踩项目”,前置心理建设做好,真遇到就不慌。
5.2 Windows 排障的几条个人习惯
说几个不花钱但很有用的习惯:
第一,统一 Node 版本管理。Windows 上没有 nvm 原生支持,我强烈建议用 nvm-windows,而不是多个 Node 版本手工切环境变量。Codex CLI 和 OpenClaw 对 Node 版本的要求可能不一致,没有统一版本管理,光是环境变量切换就够折腾半天。
第二,配置全部备份。~/.codex/config.toml、OpenClaw 的.env、网关的配置文件,改动前全部复制一份带时间戳的备份。这次排障到后面我改过好几轮 base_url,没有备份的话很可能改到自己也分不清哪个版本才是对的。
第三,养成“先验证再改配置”的纪律。每次修改配置之后,不急着跑完整任务,先跑一个最小请求确认这个变量真的生效。比如改了 base_url,就先用curl.exe打一下网关,确认返回正常,再跑 OpenClaw。直接跑完整任务的问题在于,如果失败你很难判断是配置没生效还是下游又有新问题。
第四,把窗口标题命名做好。同时开网关、OpenClaw、Codex 多个窗口时,日志全混在一起会非常痛苦。我在 PowerShell 里用$Host.UI.RawUI.WindowTitle = "gateway"区分窗口,报错的归属一眼就能看出来。
第五,遇到本地服务相关的问题,永远先看“这个服务有没有在听端口”,再谈别的。很多所谓“玄学报错”最后都落在最简单的端口或进程问题上,基础排查反而最有效。
这次从unable to locate the codex cli binary一路查到local proxy failed,最后恢复模型链路,个人最大的感受是:Windows 上编排多个 Node CLI 工具时,工具链本身的环境一致性往往比工具功能更先决定成败。如果你也在同一台 Windows 机器上折腾 Codex、OpenClaw、本地模型,建议一开始就把 PATH、base_url、端口监听这些东西固化成一键检查脚本。每次动手改配置前先跑一遍,能省掉大半的连环故障。最后分享一个小技巧:遇到 local proxy failed 这类网关错误,先别急着怀疑上游模型挂了,先去确认本地监听端口上的进程是谁——很多时候问题根本不是模型的问题,而是本地的转发服务还没醒过来。