跳到主内容
芝麻园地
EN
← 返回博客
#codex#pnpm#windows#troubleshooting#tooling

Codex 装好了却跑不起来:pnpm 静默漏装 141MB 平台包的一次完整排查

记录 Windows 下 codex 命令报 Missing optional dependency @openai/codex-win32-x64 的完整排查:pnpm 对可选依赖下载失败静默忽略、网络无法持续下载超过 60MB 的文件,以及如何用 curl 断点续传 + 手动补装平台包救回安装。

编程快车 13 分钟

结论先行

在 Windows 上装好 OpenAI Codex CLI(pnpm add -g @openai/codex)后,一运行就报:

Error: Missing optional dependency @openai/codex-win32-x64. Reinstall Codex: pnpm add -g @openai/codex@latest

按提示重装了两三次都失败。最终定位到两个坑叠加:

  1. pnpm 对可选依赖(optionalDependencies)下载失败是静默的——Codex 的 Windows 平台二进制包(@openai/codex-win32-x64,141MB)没下载成功,pnpm 也照常完成安装,运行时才暴露。
  2. 网络对持续大文件下载不稳定——连接在约 60~66MB 处被切断,而 pnpm 每次重试都从零开始下载,永远到不了 141MB。

解决办法:用支持断点续传的 curl -C - 把平台包完整拉下来,再手动补装进 pnpm 的全局虚拟 store。全程不需要重装 Codex 本体。

症状:命令存在,一运行就抛异常

$ codex
file:///C:/Users/peini/AppData/Local/pnpm/global/v11/.../node_modules/@openai/codex/bin/codex.js:107
  throw new Error(
        ^
Error: Missing optional dependency @openai/codex-win32-x64. Reinstall Codex: pnpm add -g @openai/codex@latest

codex 命令本身存在(shim、主包都装好了),但启动脚本 codex.js 在运行时找不到对应平台的二进制包,直接抛异常。报错信息还很贴心地给了重装命令。

第一次误判:按提示重装

执行 pnpm add -g @openai/codex@latest,发现它在下载 @openai/codex@0.153.4-win32-x64(141.49 MB)时,进度到约 60~66MB 就报 operation timed out,然后从头开始重试,再断,再从头…… 官方 npm 源、npmmirror 镜像源都是同样的规律,说明不是源的问题,是本机网络对长连接不稳。

观察到的关键现象:

  • pnpm 每次重试,下载进度都从 0 重新计数,不支持断点续传;
  • 失败的是可选依赖,pnpm 最终仍然以「安装成功」收场,没有任何失败提示;
  • 检查 node_modules/.pnpm/ 虚拟 store:@openai+codex@0.153.4 在,但平台包目录根本不存在。

再查 node_modules/.modules.yaml 里的 skipped 列表,居然没有列出 win32-x64——元数据声称它被安装了,文件系统却告诉我们没有。信任文件系统,别信元数据。

根因:两个坑叠在一起

坑一:可选依赖失败被静默忽略

Codex CLI 的发布方式:主包 @openai/codex(纯 JS 启动器)把各平台的 Rust 二进制拆成独立的可选依赖包(@openai/codex-win32-x64、-darwin-arm64、-linux-x64……),运行时按 process.platform + arch 解析对应包。这种模式很常见(esbuild、sharp 都这么干)。

问题是:可选依赖下载失败,pnpm 默认不报错。codex.js 的 require.resolve("@openai/codex-win32-x64/package.json") 找不到包,只能抛 Missing optional dependency。

坑二:网络扛不住 60MB+ 的长连接,而 pnpm 重试从零开始

两三个源都断在同样的量级,说明是本机网络的持续传输不稳定(常见于代理、弱网、运营商限速场景)。pnpm 的 fetch 重试是「整包重下」,不是「从断点续传」,所以只要单次下载超过网络能扛的阈值,就永远装不完。

解决方案:curl 断点续传 + 手动补装

第 1 步:curl 拉包(关键:-C - 断点续传)

curl -L -C - --retry 10 --retry-all-errors --retry-delay 3 \
  -o codex-win32-x64-0.153.4.tgz \
  "https://registry.npmmirror.com/@openai/codex/-/codex-0.153.4-win32-x64.tgz"

-C - 让 curl 在连接断开后从已下载的字节位置继续,断多少次都最终能拉完整。实际下载断了几次,但文件完整到达:141,495,386 字节。

第 2 步:解压验证

tar -xzf codex-win32-x64-0.153.4.tgz
# 确认关键二进制存在:
# package/vendor/x86_64-pc-windows-msvc/bin/codex.exe

第 3 步:放进 pnpm 全局虚拟 store(注意命名规则)

pnpm 虚拟 store 的目录名规则是 @scope+name@version,其中 version 是该包 package.json 里的完整版本字段。平台包的 version 是 0.153.4-win32-x64,所以规范目录名是:

node_modules/.pnpm/@openai+codex@0.153.4-win32-x64/node_modules/@openai/codex-win32-x64/

注意:不是 @openai+codex-win32-x64@0.153.4。我最初按「包名 + 版本」直觉建错了目录,导致 require.resolve 依旧失败。

Codex 启动器是从 node_modules/.pnpm/@openai+codex@0.153.4/node_modules/ 向上解析依赖的,所以要在它的 node_modules 下建一个指向平台包的链接:

mklink /J "…\@openai+codex@0.153.4\node_modules\@openai\codex-win32-x64" "…\@openai+codex@0.153.4-win32-x64\node_modules\@openai\codex-win32-x64"

这里踩了一个 PowerShell 的坑:New-Item -ItemType Junction 生成的链接不可用(类型变成了符号链接、目标路径被改写成错误的相对路径),换 cmd /c mklink /J 用绝对路径一次成功。Windows 上建目录链接,用 mklink /J,别用 PowerShell 的 New-Item。

第 5 步:验证

# 1. 解析验证
node -e "const {createRequire}=require('node:module'); const r=createRequire('…/bin/codex.js'); console.log(r.resolve('@openai/codex-win32-x64/package.json'))"
# 2. 实跑验证
codex --version   # → codex-cli 0.153.4 ✓

期望:给 pnpm 的愿望清单

这次经历最难受的两点,恰好都是 pnpm 可以改进的:

  1. 下载支持断点续传:请求带 Range 头、重试从上次断点继续,大包(几十上百 MB)在弱网环境下就不至于永远装不完。curl 一个 -C - 就解决的事,包管理器不该没有。
  2. 可选依赖失败要显式告警:安装失败可以继续(可选嘛),但至少在安装结束时明确提示「以下可选依赖下载失败:@openai/codex-win32-x64(141.49MB)」,而不是让人在运行时报错后才回头查。npm install 对这种情况也有同样的问题。
  3. 顺带一个元数据一致性问题:.modules.yaml 的 skipped 列表与实际落盘不一致,排查时一度被误导,建议安装完成后校验「声明的已安装项」与「实际文件」是否一致。

在 pnpm 支持之前,我们自己可以做的:

  • 装完带原生二进制的包,立刻跑一次 --version 验证,别等用的时候才发现;
  • 大包先预热:遇到几十 MB 以上的包下载失败,先 curl -C - 拉 tarball,再喂给包管理器或手动补装;
  • 换网络通道:代理 / 镜像源 / 网络环境变化后重试,往往一次就过。

避坑清单

坑 表现 规避
可选依赖静默失败 安装「成功」,运行时才报 Missing dependency 装完立刻跑 --version 验证
网络长连接不稳 大包下载到 60MB 左右断流 curl -C - 断点续传拉包
pnpm 重试从头下 每次重试进度归零,永远下不完 换通道 / 预热 tarball
虚拟 store 目录名猜错 require.resolve 仍失败 目录名按 package.json 的 version 字段拼 @scope+name@version
PowerShell New-Item 建链接不可用 生成错误的符号链接、目标错乱 用 cmd /c mklink /J 绝对路径
元数据与实际不一致 .modules.yaml 声称已装,文件不存在 以文件系统为准,别信元数据