你给 Codex 派一个 task,它卡在 “Setting up environment” 永远过不去。日志里要么是 npm ERR! 401 Unauthorized,要么 python: command not found,要么 10 分钟挂机最后报 Setup timed out。Codex 根本没读到你的代码,没出 plan——sandbox 连 pnpm install 都没跑到。
Sandbox 跑的是极简 base image + 你的 setup.sh(或 .codex/setup.sh)。绝大多数 setup 失败是六类原因之一:运行时版本不对、私有 registry 缺 token、装得太慢、缺原生编译依赖、缺系统包、setup 脚本静默失败。下面拆解每类怎么识别,加一份能解决 90% 项目的 setup 模板。
常见原因
按命中率从高到低:
1. 项目的 Node/Python 版本和 Codex base image 不匹配
Codex sandbox 一般是 Node 20 + Python 3.11。你的项目要 Node 18(lockfile 是它生成的)或 Python 3.12(用了 match 语法),npm install 报 engine 不匹配,或者 import 直接崩。
如何判断:日志里搜 EBADENGINE、Unsupported engine、SyntaxError: invalid syntax(Python)、requires: node@<version>。setup 脚本开头加一行 node -v && python --version,版本错了一眼看出来。
2. 私有 npm / pip registry 缺 token
你的 package.json 从 GitHub Packages 或 Verdaccio 拉 @your-org/private-pkg。Codex 没有 .npmrc token,npm install 直接 401 Unauthorized。
如何判断:setup 日志搜 401、403、Unauthorized、Not Found,配上你的 registry URL。失败的包名以 @your-org/ 开头就是这个原因。
3. 安装超过 sandbox 超时
Codex sandbox 有 setup 超时(一般 5-10 分钟)。大 monorepo 冷装 pnpm install,或者为 Playwright 拉 500MB Docker base,会直接撞上限。
如何判断:日志结尾是 timeout 而不是某个具体包报错。本地同样安装如果冷启动要 >5 分钟,sandbox 必撞超时。
4. 原生编译依赖缺
node-gyp、sharp、node-canvas、lxml、pillow、psycopg2-binary 需要 gcc、python-dev、libpq-dev 等系统包。Codex base image 很瘦,这些默认不装。
如何判断:搜 gcc: command not found、Python.h: No such file、pg_config: command not found,或者 node-gyp 的 stack。原生编译失败往往日志一长串,但越过前 50 行就清楚。
5. Setup 脚本失败后静默继续
你的 setup.sh 写的是 npm install && npm run build。install 部分成功(有 warning 但没 error),脚本继续往下,build 在某个莫名其妙的地方挂掉——其实根因是上一步。
如何判断:日志里第一个 error 不是根因。脚本顶部加 set -euo pipefail 重跑,真正的错就浮出来。
6. 出站网络被限或被禁
Codex sandbox 限制出站流量。某些 registry(公司 VPN 内自建的那种)根本访问不到,GitHub raw 下载可能被限速。
如何判断:getaddrinfo ENOTFOUND、connect ECONNREFUSED、或某个非公网域名的 503 Service Unavailable。换个网络试同 URL——能通就是 sandbox 出站被限。
最短修复路径
按收益从高到低。前 3 步覆盖”版本 / token / 原生依赖”三大件。
Step 1:固定运行时版本,让 Codex 能识别
repo 根加:
# .nvmrc
20.11.0
# .python-version
3.11.7
Codex sandbox 会自动检测、nvm use / pyenv install 切到指定版本再 npm install。要别的版本就改这里。
Bun / Deno / Java 同理:.bun-version、deno.json、.java-version。
Step 2:写一个 fail-fast 的 setup.sh
放 .codex/setup.sh(或 Codex 配置指定的位置):
#!/usr/bin/env bash
set -euo pipefail # 首错即停 / 禁未定义变量 / pipe 失败也停
echo "=== Runtime versions ==="
node -v
python --version || echo "python not present"
echo "=== System deps ==="
which gcc make pkg-config || true
echo "=== Install ==="
# 用 ci 不用 install——确定性 + 更快
npm ci --no-audit --no-fund
echo "=== Build ==="
npm run build --if-present
echo "=== Setup done ==="
set -euo pipefail 这行最关键——脚本会在首个错误处停下,而不是级联错下去把你绕晕。
Step 3:把 registry token 放进 Codex secrets
私有 npm 包:
- 在 registry 生成只读 token(GitHub Packages 的话是 PAT +
read:packages权限)。 - Codex 设置 → Secrets 加
NPM_TOKEN=ghp_xxx。 - setup.sh 里从 env 生成
.npmrc:
cat > ~/.npmrc <<EOF
@your-org:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${NPM_TOKEN}
always-auth=true
EOF
pip:
export PIP_INDEX_URL="https://${PYPI_USER}:${PYPI_TOKEN}@pypi.your-org.com/simple/"
pip install -r requirements.txt
绝对不要 commit .npmrc / .pip.conf——只从 env 生成。
Step 4:原生编译依赖提前装
package.json 里有 sharp、canvas、bcrypt、node-gyp 系列就先装构建工具:
# Ubuntu / Debian base(Codex sandbox 多数是这个)
apt-get update -qq
apt-get install -y --no-install-recommends \
build-essential \
python3-dev \
libpq-dev \
libcairo2-dev libjpeg-dev libpango1.0-dev libgif-dev \
libvips-dev # sharp 需要
Python 的 psycopg2、lxml、pillow 加 libpq-dev、libxml2-dev、libjpeg-dev。
能换预编译版本就换(sharp 用 --platform=linux --arch=x64 标志,psycopg2 换 psycopg2-binary),直接跳过原生编译。
Step 5:让 install 更快,避开超时
把 npm install 换成 npm ci(跳过依赖解析、更快 + 确定)。pnpm / yarn 同理:
# pnpm
pnpm install --frozen-lockfile --prefer-offline
# yarn
yarn install --frozen-lockfile --offline-mirror=.npm-offline
Monorepo 就只装 task 涉及的那块:
# pnpm workspace,只 build API 包
pnpm --filter=@your-org/api install
harness 支持的话把 node_modules 缓存——首次慢、之后秒级。
Step 6:本地先验证 setup,再派 task
别在 Codex 日志里调环境问题,本地复现一遍:
docker run --rm -it -v $(pwd):/repo -w /repo node:20-bookworm bash
# 容器里:
bash .codex/setup.sh
容器里跑通 sandbox 也能通(前提是 secrets 配齐了)。本地都跑不通,就在本地全工具链下修,比对着 Codex 的 setup-only 日志省一万倍精力。
预防建议
- 永远在
.nvmrc/.python-version里固定运行时版本——不要赌 sandbox 猜得对 - 每个
setup.sh顶部加set -euo pipefail,让失败 loud 不要 silent - registry token 走 Codex secrets,setup.sh 里从 env 生成
.npmrc,绝不 commit - 原生依赖优先用预编译(
sharp、psycopg2-binary),别让 sandbox 跑源码编译 - 用
npm ci/pnpm install --frozen-lockfile——更快、确定、严格按 lockfile - 派 task 前先在干净 Docker 容器里跑通 setup 脚本
- setup 控制在 3 分钟内,给 sandbox 超时留余量