1. npm ENOTEMPTY 报错到底卡在哪一步:claude-code 全局安装失败的真实场景
npm error code ENOTEMPTY配合syscall rename和directory not empty,是 Node.js 全局包升级时最典型的文件系统级冲突。它跟网络超时、版本不兼容完全不是一类问题——npm 已经下载完了新包,只是在替换旧目录的最后一步被挡住了。理解这一点,排查方向就不会跑偏。
报错长这样:
npm error code ENOTEMPTY npm error syscall rename npm error path /opt/homebrew/lib/node_modules/@anthropic-ai/claude-code npm error dest /opt/homebrew/lib/node_modules/@anthropic-ai/.claude-code-2DTsDk1V npm error errno -66 npm error ENOTEMPTY: directory not empty, rename '/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code'npm 安装全局包的流程分三步:先把旧版本目录重命名成一个带随机后缀的临时目录(比如.claude-code-2DTsDk1V),再把新版本写进原路径,最后删掉临时目录。rename系统调用要求目标目录为空或不存在,一旦旧目录里有 npm 管不到的文件——被进程占用的.node文件、root 权限写入的缓存、编辑器生成的临时文件——重命名就会失败,errno -66 直接抛出。
哪些人最容易撞上这个错?我整理了几类高频场景:
| 场景 | 触发原因 | 典型表现 |
|---|---|---|
| Homebrew 装 Node 后升级 | /opt/homebrew/lib/node_modules权限受 brew 管理 | 反复npm install -g都报同一路径 |
| 切换 Node 版本(nvm/fnm) | 旧版本全局目录残留,新版本 prefix 指向不同 | 换版本后首次安装必报 |
| 多账号或配置被改 | 安装中途 Ctrl+C,临时目录没清干净 | 报错路径带.claude-code-xxxx后缀 |
| 企业内网/CI 环境 | 缓存层复用导致旧目录未清 | 偶发,重跑有时能过 |
| 曾用 sudo 安装 | 目录属主变成 root,普通用户删不掉 | ls -la看到 owner 是 root |
判断自己属于哪一类,最快的办法是看报错里的path和dest。如果dest已经存在且非空,说明上一次安装中断留下了临时目录;如果path本身删不掉,多半是权限或进程占用。
这里有个容易被忽略的点:很多人第一反应是npm install -g @anthropic-ai/claude-code --force,但--force只跳过部分校验,不会帮你删掉被占用的文件。真正要解决的是「谁占着这个目录」和「谁有权删这个目录」。
还有一个认知误区值得说清楚。ENOTEMPTY 不是 claude-code 独有的问题,任何全局 npm 包在升级时都可能遇到,只是 claude-code 更新频繁、体积不小,撞上的概率更高。所以下面这套排查思路,换成@openai/codex、@google/gemini-cli一样适用。
我在一台 M 系列 Mac 上复现过完整过程:先用 Homebrew 装 Node 20,npm install -g @anthropic-ai/claude-code成功;然后nvm install 22 && nvm use 22,再装同一个包,立刻报 ENOTEMPTY,路径正是/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code。原因很清楚——nvm 切换后npm config get prefix变了,但旧目录还在原地,npm 尝试重命名时发现里面有 brew 写入的只读文件。
所以排查顺序建议固定为:先确认进程没占用,再确认权限归属,最后才动缓存。顺序反了会白折腾。
2. 修完 ENOTEMPTY 之后:用 TaoToken 统一 Key 通道接管 claude-code 的模型请求
目录清理只是让 claude-code 能装上,装完之后你还要面对第二个问题:模型请求走哪条通道。默认情况下 claude-code 会读环境变量里的 Anthropic 相关配置,如果你同时用 Codex、Cline、Cursor 好几个工具,每个都配一遍 Key,管理成本很高,而且一旦某个 Key 额度用完,得挨个改。
TaoToken 在这里的角色是统一入口。它提供一个兼容 Anthropic 接口规范的 Base URL,你把 claude-code 的请求指向它,再用一个 Key 管理所有模型的调用。官网入口是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 地址是 https://taotoken.net/api ,注意 API 地址不带 UTM 参数,配置时别多写。
为什么要在修完 ENOTEMPTY 之后立刻做这件事?因为 claude-code 的配置文件和 npm 全局目录是两套东西。你rm -rf删掉的是/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code,但用户级配置在~/.claude/settings.json或项目级.claude/settings.json,删包不会动它。如果之前配置写错了,重装多少次都没用。反过来,先把 Key 通道理顺,再验证安装,能少走一轮弯路。
TaoToken 适合谁?三类人最明显:一是同时用多个 AI 编码工具、想统一管 Key 的;二是团队里需要共享一套调用额度、又不想每人发 Key 的;三是经常切换模型做对比测试、不想每次改环境变量的。
需要说清楚的是,TaoToken 不是编辑器,也不替代 claude-code 本身。它是请求转发层,claude-code 还是那个 claude-code,只是它发出的模型请求不再直连,而是先到 TaoToken 再分发。这个定位搞混了,后面配置会一头雾水。
拿到 Key 的路径:进 https://taotoken.net/api-keys ,登录后创建 API Key,复制出来。这个 Key 后面要填进 settings.json 的env字段里。如果你还没决定用哪个模型,可以先到 https://taotoken.net/models 看一眼可用列表,再决定 Model ID 填什么。
有一点必须提醒:Key 只显示一次,创建后立刻复制保存。丢了只能重建,重建后旧 Key 立即失效,所有引用它的配置文件都要同步更新。我见过有人把 Key 写进 Git 仓库然后推到公开分支,这种操作等于把额度白送人,务必用环境变量或本地配置文件承载。
3. 可复制配置:settings.json 骨架 + npm 目录清理命令
这一节给两段能直接抄的东西。第一段是 claude-code 的 settings.json 配置骨架,第二段是 ENOTEMPTY 的清理命令。两段配合用,先清目录再写配置。
先看 settings.json。claude-code 支持用户级和项目级配置,用户级路径是~/.claude/settings.json,项目级是项目根目录下的.claude/settings.json。项目级优先级更高,适合给不同项目配不同模型。骨架如下:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_AUTH_TOKEN": "sk-你的TaoToken密钥", "ANTHROPIC_MODEL": "claude-sonnet-4-20250514" }, "permissions": { "allow": [], "deny": [] } }三个字段的作用要分清:ANTHROPIC_BASE_URL决定请求发到哪,填 TaoToken 的 API 地址;ANTHROPIC_AUTH_TOKEN是鉴权凭证,填你在 api-keys 页面创建的 Key;ANTHROPIC_MODEL指定默认模型,具体可用的 Model ID 到 https://taotoken.net/models 查,别照抄我这里的示例值,模型会更新。
如果你用 Codex,配置在~/.codex/auth.json,结构不同但三件套一样——Base URL、Key、Model ID 缺一不可:
{ "OPENAI_BASE_URL": "https://taotoken.net/api", "OPENAI_API_KEY": "sk-你的TaoToken密钥", "OPENAI_MODEL": "gpt-4o" }Cline 或 Roo Code 这类 VS Code 插件,配置在插件设置面板里,同样是填 Base URL、API Key、Model ID 三项。Cline 的 MCP 配置如果单独走,记得 MCP server 的地址和模型请求地址是两回事,别混填。
现在看 ENOTEMPTY 的清理命令。核心思路是:先停进程,再删目录,再清缓存,最后重装。按顺序执行:
# 1. 确认没有 claude 进程占用 ps aux | grep claude # 2. 有的话先结束(macOS/Linux) pkill -f claude # 3. 进入报错路径的父目录 cd /opt/homebrew/lib/node_modules/@anthropic-ai/ # 4. 删除旧目录和所有临时目录 rm -rf claude-code rm -rf .claude-code-* # 5. 清 npm 缓存 npm cache clean --force # 6. 重新安装 npm install -g @anthropic-ai/claude-code@latest如果你的 prefix 不是 Homebrew 路径,用这条命令查真实路径,把上面第 3 步替换掉:
npm config get prefix # 输出类似 /usr/local 或 ~/.npm-global # 则目录是 <prefix>/lib/node_modules/@anthropic-ai/权限问题导致的删不掉,先修属主再删:
sudo chown -R $(whoami) $(npm config get prefix)/lib/node_modules/@anthropic-ai/ rm -rf $(npm config get prefix)/lib/node_modules/@anthropic-ai/claude-code rm -rf $(npm config get prefix)/lib/node_modules/@anthropic-ai/.claude-code-*Windows 上用 PowerShell,管理员身份运行:
Remove-Item -Recurse -Force "$env:APPDATA\npm\node_modules\@anthropic-ai\claude-code" Remove-Item -Recurse -Force "$env:APPDATA\npm\node_modules\@anthropic-ai\.claude-code-*" npm cache clean --force npm install -g @anthropic-ai/claude-code@latest一劳永逸的做法是配置用户级全局目录,避开系统目录的权限坑:
mkdir -p ~/.npm-global npm config set prefix '~/.npm-global' echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> ~/.zshrc source ~/.zshrc npm install -g @anthropic-ai/claude-code@latest配完之后npm config get prefix应该输出/Users/你的用户名/.npm-global,以后所有全局包都装这里,不再碰 Homebrew 或系统目录,ENOTEMPTY 基本绝迹。
4. 验证请求:从 claude --version 到模型对话跑通
配置写完不算完,得验证两件事:claude-code 本身装好了,以及模型请求能通过 TaoToken 正常返回。分两步走。
第一步,验证安装:
claude --version # 期望输出类似:1.0.xx (Claude Code)如果这条还报 ENOTEMPTY,说明目录没清干净,回到第 3 节重来。如果报command not found,是 PATH 没配好,检查npm config get prefix的 bin 目录有没有加进 PATH。
第二步,验证模型请求。最直接的方式是启动 claude-code 发一条消息:
claude # 进入交互界面后输入: > 用一句话说明你当前使用的模型如果配置正确,会正常返回内容。如果返回 401,说明 Key 有问题;如果返回连接错误,说明 Base URL 写错了。这两个错误的排查放到第 5 节。
不想进交互界面的话,可以用 curl 直接打 TaoToken 的接口,验证 Key 和地址是否通:
curl https://taotoken.net/api/v1/messages \ -H "x-api-key: sk-你的TaoToken密钥" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{ "model": "claude-sonnet-4-20250514", "max_tokens": 64, "messages": [{"role": "user", "content": "ping"}] }'返回 JSON 里带content字段就说明通道通了。这一步能排除 claude-code 本身的干扰,直接定位是 Key 问题还是工具问题。
验证通过后,建议做一次完整的编码任务测试,比如让 claude-code 读一个文件并改一行:
cd ~/your-project claude > 读取 package.json,把 version 字段改成 1.0.1观察它是否能正常调用工具、读写文件。这一步过了,说明安装、配置、通道三件事全部就绪。
如果你更想先在网页端确认模型可用性,可以到 https://taotoken.net/models 的对话入口发一条测试消息,确认 Key 有额度、模型能响应,再回到命令行配置。这样能把「Key 无效」和「配置写错」两类问题分开。
实测下来,最容易出问题的环节是ANTHROPIC_MODEL填了一个不存在的 Model ID。claude-code 不会在启动时报错,而是在发请求时返回模型不存在。所以填之前一定去模型列表页核对,别凭记忆写。
5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth
这一节把配置过程中最常撞的四个报错拆开讲,每个都给定位方法和修复动作。
401 Unauthorized
API Error: 401 {"type":"error","error":{"type":"authentication_error","message":"invalid x-api-key"}}原因只有三种:Key 复制时带了空格或换行、Key 已失效、Key 填错了字段。检查~/.claude/settings.json里ANTHROPIC_AUTH_TOKEN的值,前后不能有空格。如果确认没写错,去 https://taotoken.net/api-keys 看这个 Key 是否还在、是否被禁用。重建一个再试。
local proxy failed / connection refused
Error: connect ECONNREFUSED 127.0.0.1:xxxx这是 Base URL 写成了本地地址,或者你本地跑了个代理但没启动。检查ANTHROPIC_BASE_URL是不是https://taotoken.net/api,别写成http://localhost:8080之类。如果你确实在用本地转发工具,确认它开着,但更推荐直接填 TaoToken 地址,少一层依赖。
reading choices / Cannot read properties of undefined (reading 'choices')
TypeError: Cannot read properties of undefined (reading 'choices')这个错通常出现在用 OpenAI 兼容格式请求 Anthropic 接口,或反过来。claude-code 走的是 Anthropic 的/v1/messages格式,返回结构里是content不是choices。如果你在 Cline 里选了 OpenAI 协议却填了 Anthropic 的地址,就会报这个。检查插件的 API 协议选项,Anthropic 接口选 Anthropic,OpenAI 接口选 OpenAI,别混。
OAuth 相关报错
OAuth error: invalid_grantclaude-code 某些版本会尝试 OAuth 登录流程。如果你已经用 API Key 配置了,还弹 OAuth,说明配置没被读到。检查 settings.json 的路径对不对——用户级是~/.claude/settings.json,不是~/.claude.json,两个文件不一样。另外确认没有环境变量覆盖,比如 shell 里 export 了ANTHROPIC_API_KEY,它会优先于配置文件。
排查清单速查:
| 报错 | 首查项 | 修复动作 |
|---|---|---|
| 401 | Key 值有无空格 | 重新复制或重建 Key |
| local proxy failed | Base URL 是否本地地址 | 改为 https://taotoken.net/api |
| reading choices | 协议选错 | Anthropic 接口选 Anthropic 协议 |
| OAuth invalid_grant | 配置文件路径 | 确认是 ~/.claude/settings.json |
| ENOTEMPTY 复现 | 目录权限/进程 | 回第 3 节清理 |
还有一个隐蔽的坑:同时装了多个版本的 claude-code,which claude指向的可能是旧版本。用which -a claude看所有路径,把多余的删掉,只留~/.npm-global/bin/claude或你 prefix 下的那个。
6. 把 Key 通道固定下来:长期编码场景的配置建议
ENOTEMPTY 修一次就够了,但 Key 通道的配置会跟着你很久。如果你打算长期用 claude-code 做编码,建议把配置做成可复用、可迁移的形式,而不是每次换机器重配一遍。
第一,把 settings.json 纳入 dotfiles 管理,但 Key 不要硬编码。用环境变量引用:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_AUTH_TOKEN": "${TAOTOKEN_API_KEY}", "ANTHROPIC_MODEL": "claude-sonnet-4-20250514" } }然后在~/.zshrc里export TAOTOKEN_API_KEY="sk-..."。这样配置文件可以进 Git,Key 留在本地。
第二,项目级配置覆盖用户级。团队项目可以在.claude/settings.json里指定该项目专用的 Model ID,比如代码审查用便宜模型、重构用强模型,互不干扰。
第三,如果你同时用 Codex 和 claude-code,把两者的 Base URL 都指向 TaoToken,Key 用同一个,额度统一管理。Codex 的~/.codex/auth.json和 claude-code 的~/.claude/settings.json各配各的,但 Key 值相同,换 Key 时两处一起改。
第四,长期跑 Agent 任务的话,关注一下 Coding Plan 的额度策略,比按次调用更适合高频场景。入口在 https://taotoken.net/coding-plan ,具体额度以页面为准。
最后回到 ENOTEMPTY 本身。这个错的本质是文件系统状态和 npm 预期不一致,跟 TaoToken、跟模型都没关系。修的时候别慌,按「停进程 → 查权限 → 删目录 → 清缓存 → 重装」的顺序走,九成情况一次解决。剩下那一成,多半是 prefix 配错了或者有多个 Node 版本在打架,用npm config get prefix和which -a node两个命令就能定位。
配置通道的时候记住三件套:Base URL 填https://taotoken.net/api,Key 从 api-keys 页面拿,Model ID 去模型列表核对。三个都对上,claude-code 就能稳定跑起来。