Claude Code 安装失败实测:npm EACCES、镜像卡 600 秒、Node 20 静默装旧版、install.sh 返回地区拦截页、原生安装器顺手卸掉 npm 版——15 条报错原文与解法
搜「claude code 安装」的人,大多数不缺教程——缺的是装到一半报错时,这句报错到底是什么意思。安装步骤本站已经有了(Claude Code 快速上手),这篇不重复,只做一件事:把安装会失败的地方在本机一个个真实复现,逐字记下报错原文、触发条件、耗时和退出码,再给出解法。
先说最意外的三条:
- Node 20 装 Claude Code 不会报错,而是静默装到 2.1.197。engines 从 2.1.198 起要求
>=22,npm 会自动挑一个旧的、满足条件的版本,没有任何警告。 - 在国内直连
curl -fsSL https://claude.ai/install.sh时,curl 退出码是 0,下载下来的却是 447,830 字节的「App unavailable in region」HTML 页面。| bash之后只会看到一句syntax error near unexpected token '<'。 - 官方原生安装器会执行
npm uninstall -g @anthropic-ai/claude-code,把你已有的 npm 版删掉,终端里一个字都不提。本次实测就这样真的删掉了本机的全局安装——下面「踩坑」一节有完整经过。

问题背景
Claude Code 现在有两条官方安装路径:
- npm:
npm install -g @anthropic-ai/claude-code。现在的包只是一个「壳」:真正的程序是平台对应的 optionalDependency(例如@anthropic-ai/claude-code-darwin-arm64,tarball 98,952,459 字节)。postinstall 脚本install.cjs会把这个原生二进制硬链接到bin/claude.exe。claude命令本身已经不是 JS 了,而是一个 Mach-O 可执行文件(2.1.284 为 226,563,088 字节)。 - 原生安装器:
curl -fsSL https://claude.ai/install.sh | bash。脚本从downloads.claude.ai下载二进制、校验 sha256,然后执行claude install,最终装到~/.local/bin/claude。
这两条路径各有各的失败面,而国内网络环境会让其中好几个失败面的报错看起来和真正的原因毫无关系。
问题分析
把「claude code 安装失败」拆开,能出问题的环节有这些:
| 环节 | 可能出的事 | 本文对应 |
|---|---|---|
| 写入全局目录 | prefix 没有写权限 | 表中 #1 |
| npm cache | cache 目录不可写 | #2 |
| registry | 连不上、DNS 解析不了、镜像慢 | #3 #4 #6 |
| 代理 | npmrc 与环境变量冲突 | #5 |
| Node 版本 | engines 门禁 | #7 #8 |
| 原生安装器 | 地区拦截、下载服务连不上、覆盖 npm 版 | #9 #10 #12 |
| 装完之后 | PATH、postinstall 没跑、用错启动方式 | #11 #13 #14 #15 |
还有一个前提容易被忽略:npm install -g 读不到项目目录里的 .npmrc。实测在仓库根目录(其 .npmrc 写着 npmmirror),隔离 HOME 之后:
$ npm config get registry → https://registry.npmmirror.com/
$ npm config get registry -g → https://registry.npmjs.org/
也就是说,全局安装实际生效的只有 ~/.npmrc、全局 npmrc 和命令行参数。排查 registry / 代理问题时,一定要用 npm config get xxx -g 看。
技术方案与选型
目标是「真实复现 + 不破坏本机」,方案如下:
- 所有安装都隔离:
--prefix、--cache、--logs-dir全部指向/tmp/cc-install-0929/下的子目录,不写~/.npm和/opt/homebrew(原生安装器那一次例外,见「踩坑」)。 - 失败面用可控的坏值触发:registry 指向
127.0.0.1:1和.invalid域名,代理指向127.0.0.1:1/:2,cache 用 0555 目录,权限用/usr/local。 - 低版本 Node 用便携版:本机的 22 / 24 / 25 都满足
>=22,于是在/tmp下解压了 Node v20.19.5(npm 10.8.2),不用 nvm,也不改系统。 - 网络对照:镜像和官方源分别用独立 cache,否则第二次会命中相同 integrity 的缓存,测不出下载时间。
排除项:
- 不测 Windows / Linux:只有一台 macOS,写不出没测过的报错原文。
- 不测
sudo npm install -g:会写系统目录,而且这是网上最常见的错误建议,不值得为它破坏本机。 - 不测 pnpm / yarn / bun:报错原文里提到了「some pnpm configs」,但本次没有复现,不下结论。
- 不调用 API:安装问题与模型无关,全程 0 次 API 调用,花费 ¥0。
实测过程
- 环境:macOS 26.3.1,Apple Silicon;Node v25.9.0,npm 11.11.0;北京直连,出口 AS4847,没有系统代理,也没有 TUN。
- 本机原有 Claude Code 2.1.280(Homebrew 下的 npm 全局安装)。npm 上的 dist-tags 为 latest 2.1.284、stable 2.1.277,所以不钉版本时装到的是 2.1.284。
- 每一组都记录了逐字命令、stdout+stderr、墙钟秒数和 exit code;带颜色的输出保留为
.ansi,截图用它渲染,没有手写的输出。 - 时间:2026-09-29 10:02–10:37。
实践效果:15 条报错原文对照表
「解法已实测」一列标 ✅ 的,是本次在本机验证过的;标「未实测」的是依据报错文案或文档给出的建议。
| # | 报错原文(节选) | 触发条件 | 墙钟 | exit | 解法 | 解法已实测 |
|---|---|---|---|---|---|---|
| 1 | npm error code EACCES … Error: EACCES: permission denied, mkdir '/usr/local/lib' |
全局 prefix 没有写权限 | 0.12s | 243 | 不要 sudo。把 prefix 换到用户可写的目录(npm config set prefix ~/.npm-global,再把 ~/.npm-global/bin 加进 PATH),或者用 Homebrew / nvm 装的 Node |
✅(所有 --prefix /tmp/... 的安装都成功) |
| 2 | Your cache folder contains root-owned files, due to a bug in previous versions of npm … sudo chown -R 501:20 "…" |
cache 目录不可写——本次属主就是自己,只是权限 0555 | 1.45s | 1 | 先 ls -ld "$(npm config get cache)",看清是属主问题还是权限位问题;只有确实是 root 属主时才 chown |
未实测(只验证了报错) |
| 3 | FetchError: request to http://127.0.0.1:1/… failed, reason: connect ECONNREFUSED 127.0.0.1:1 |
registry 端口不通 | 3.16s(缩短重试参数)/ 70.15s(默认参数) | 1 | npm config get registry -g 找出是谁配的,改掉 |
✅ |
| 4 | network request to https://nonexistent-cc-0929.invalid/… failed, reason: getaddrinfo ENOTFOUND |
registry 域名解析不了 | 3.86s | 1 | 同上 | ✅ |
| 5 | request to https://registry.npmjs.org/… failed, reason: connect ECONNREFUSED 127.0.0.1:2 |
npmrc 里残留的 proxy=/https-proxy= 压过了环境变量 HTTPS_PROXY |
0.13s | 1 | npm config get proxy -g 和 npm config get https-proxy -g 一起查,删掉残留项 |
✅(换成空 userconfig 后恢复只读 env) |
| 6 | 无报错,只是卡住:147.22s 装完 / 超过 600s 被我 kill | 本机经 npmmirror 下载平台包 | 147s / >600s | 0 / kill | 加 --registry https://registry.npmjs.org 对照一次 |
✅ 本机官方源 10.74s / 12.20s(各 2 个样本) |
| 7 | 没有报错:added 2 packages in 21s,然后 claude --version → 2.1.197 |
Node <22 且不钉版本号 | 21s | 0 | 升级到 Node ≥22;装完先看 claude --version |
✅(Node 25 下装到 2.1.284) |
| 8 | npm warn EBADENGINE Unsupported engine / 加 --engine-strict 后变成 npm error code EBADENGINE |
Node <22 钉 @2.1.284 |
5.51s / 0.17s | 0 / 1 | 同上 | ✅ |
| 9 | bash: line 1: syntax error near unexpected token '<'(HTML 的 <title>App unavailable in region</title>) |
国内直连 claude.ai/install.sh |
6.27s / 3.00s(两次) | curl 0 | 先落盘 -o install.sh 再 head -1 看一眼;在国内改走 npm 路线,或者通过代理 |
✅(走代理后拿到 9,704 字节的真脚本) |
| 10 | curl: (28) Failed to connect to downloads.claude.ai port 443 after 75595 ms |
国内直连下载服务 | 75.63s | 28 | 走代理 | ✅(51.74s 装好) |
| 11 | Native installation exists but ~/.local/bin is not in your PATH |
原生安装完成后 | 51.74s | 0 | 按提示把 ~/.local/bin 加进 PATH(安装器不会自动改 ~/.zshrc) |
✅ |
| 12 | 没有任何输出,后台执行了 npm uninstall -g @anthropic-ai/claude-code |
之前用 npm 全局装过,再跑原生安装器 | 28.76s(安装器整体) | 0 | 想保留 npm 版就别跑原生安装器;跑了的话,用 which -a claude 确认现在用的是哪一份 |
✅(假 npm + strings 双证据,见下) |
| 13 | zsh:1: command not found: claude |
PATH 里没有 $(npm prefix -g)/bin 或 ~/.local/bin |
0.11s | 127 | 把对应的 bin 目录加进 PATH | ✅ |
| 14 | Error: claude native binary not installed. … node node_modules/@anthropic-ai/claude-code/install.cjs |
--ignore-scripts 安装,postinstall 没跑 |
0.84s | npm 0,claude 1 | 去掉 --ignore-scripts 重装,或者手动 node "$(npm root -g)/@anthropic-ai/claude-code/install.cjs" |
未实测 |
| 15 | TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".exe" |
按旧教程 node …/bin/claude --version |
— | 1 | 直接执行 …/bin/claude --version |
✅ |
下面挑几条展开。
#1 #2:权限类报错,第二条的诊断是错的
--prefix /usr/local 是最典型的 EACCES:0.12 秒就失败,exit 243,因为 mkdir '/usr/local/lib' 这一步就被拒了,所以没有留下半成品(/usr/local 实测与基线一致)。

cache 那条更值得注意。我给 cache 目录设了 0555,属主就是当前用户 duoduo,npm 的报错却是「你的 cache 目录里有 root 拥有的文件,这是旧版 npm 的 bug」,并建议 sudo chown -R 501:20。照做修不好,因为问题根本不在属主。先 ls -ld 看清楚再动手。

#3 #4 #5:连不上时,npm 默认会等 70 秒
npm 默认 fetch-retries=2,退避 10s → 60s。registry 端口被拒(ECONNREFUSED)时,默认参数下 3 次尝试一共 70.15 秒才报错;把重试参数调短后是 3.16 秒。DNS 解析失败(ENOTFOUND)同样是 3 次尝试。看起来像「卡住了」的安装,不一定真卡住,可能只是在重试。
代理那条实测了 5 组:
| 环境变量 | 隔离 npmrc | npm 实际连的 |
|---|---|---|
HTTPS_PROXY=:1 |
空 | :1 |
HTTPS_PROXY=:1 |
https-proxy=:2 |
:2 |
HTTPS_PROXY=:1 |
proxy=:2 |
:2 |
| 无 | proxy=:2 |
:2(proxy= 对 https registry 同样生效) |
小写 https_proxy=:1 |
空 | :1 |
结论:npmrc 里的代理配置优先于环境变量,哪怕写的只是 proxy=。报错文案里的 URL 是 registry,端口却是代理的端口,全文也没有点明这是代理的问题——很容易以为是 registry 挂了。另外,npm 走不走代理和 Node 内置 fetch 是两回事,后者不认 HTTPS_PROXY,见这篇。
#6:镜像 vs 官方源(只有 2 个样本)
| 组 | registry | 墙钟 | exit |
|---|---|---|---|
| 镜像第 1 次 | npmmirror(经 ~/.npmrc) |
147.22s | 0 |
| 镜像第 2 次 | npmmirror | >600s,被 kill | — |
| 官方源第 1 次 | registry.npmjs.org 直连 | 10.74s | 0 |
| 官方源第 2 次 | registry.npmjs.org 直连 | 12.20s | 0 |
npm 调试日志显示,镜像慢在 cdn.npmmirror.com 上那个 99MB 的平台包:139,231ms;官方源同一个包是 6,881ms / 7,619ms。用 curl 单独拉镜像的同一个文件,125.7 秒后以 exit 35(SSL 连接错误)失败;官方源 7.66 秒,12.9 MB/s。
适用范围要说清楚:这是 2026-09-29 上午、北京、AS4847 出口、每组 2 个样本的结果,不能推广成「镜像一定比官方源慢」。但它说明一件事:「国内就该用镜像」不是无条件成立的,装不动时拿 --registry https://registry.npmjs.org 对照一次,成本只要十几秒。另外,被 kill 的那次在 prefix 里留下了只有 lib/ 的半成品目录。
#7 #8:Node 20 不会报错,而是装旧版
这一条是本次最隐蔽的。查 npm 上每个版本的 engines:2.1.197 及以前是 >=18.0.0,从 2.1.198 起是 >=22.0.0。在 Node v20.19.5 下:
- 不钉版本:
added 2 packages in 21s,exit 0,装到的是 2.1.197,没有任何警告; - 不钉版本 +
--engine-strict:一样装到 2.1.197,照样没警告; - 钉
@2.1.284:只有npm warn EBADENGINE,exit 0,装出来的二进制照样能跑(它是原生程序,不依赖 Node); - 钉
@2.1.284+--engine-strict:npm error code EBADENGINE,exit 1,0.17 秒,并留下一个空的lib/。

所以如果你在 Node 18/20 上装完发现「文档里的新功能怎么没有」,先看一眼 claude --version。
#9 #10 #11 #12:原生安装器的四个坑
**地区拦截页被当成脚本。**国内直连时 claude.ai/install.sh 先 302 跳到 claude.com/app-unavailable-in-region,再返回 200 text/html。curl -fsSL 的 -f 只对 HTTP 错误码生效,所以 curl 退出码是 0。10:19 和 10:37 两次复现结果一致,都是 447,830 字节。

**下载服务连不上。**用代理拿到真脚本后,在直连下运行:脚本在第一步请求 downloads.claude.ai/claude-code-releases/latest 时,curl 75,595 ms 后报 (28)。脚本里其实写了友好提示(not available in your region),但因为 set -e 加命令替换,脚本直接以 curl 的 28 退出,友好提示没有机会显示。
**走代理能装好,但 PATH 要自己加。**51.74 秒装好 2.1.284,产物是 ~/.local/bin/claude → ~/.local/share/claude/versions/2.1.284(约 216MB)。安装器提示 ~/.local/bin is not in your PATH,给出了 echo … >> ~/.zshrc,但不会替你执行。
**它会卸掉你的 npm 版。**原生二进制的 install 子命令会检查是否存在 npm 全局安装,如果有,就执行 npm uninstall -g @anthropic-ai/claude-code。我用两种互不依赖的方法确认了这一点:
- 假 npm:在 PATH 最前面放一个只记录参数、然后
exit 0的npm,同时设置npm_config_prefix指向隔离目录做双保险,再执行claude install。记录下来的调用只有一条:[uninstall] [-g] [@anthropic-ai/claude-code]。 - strings:在 2.1.284 二进制的偏移 189061680 处找到
qe("npm",["uninstall","-g",e],{cwd:process.cwd(),…}),成功后调用t(\Removed global npm installation of ${e}`)`——这是写日志,不是终端输出,所以屏幕上看不到。
安装器的终端输出从头到尾只有 Checking installation status… / Installing… / ✔ Claude Code successfully installed!。从产品角度这大概是有意的「迁移」,但如果你同时依赖 npm 版(比如脚本里写死了 /opt/homebrew/bin/claude),它就会悄无声息地失效。
#14 #15:装完了却跑不起来
--ignore-scripts 安装:npm exit 0,看上去一切正常(安装加运行整组 0.84 秒),但 bin/claude.exe 只有 500 字节,是个占位文件,一运行就报 Error: claude native binary not installed.,exit 1。报错文案本身写得很清楚,给出了手动跑 postinstall 的命令。

老教程里的 node $(npm root -g)/@anthropic-ai/claude-code/…/claude 这种写法已经不能用了:node …/bin/claude --version 报 ERR_UNKNOWN_FILE_EXTENSION ".exe",因为那是 Mach-O 二进制,直接执行即可。
装好、能跑之后,如果登录时报 Invalid API key / Not logged in,那是认证问题,不是安装问题,见 Claude Code 认证报错实测。
未复现 / 不适用
--omit=optional未复现:报错文案说它会导致缺少二进制,但在 npm 11.11.0 的全局安装里,平台包照样装上了,claude --version正常。- 「换个 Node 版本就跑不起来」不适用:产物是原生二进制,
otool -L只链接了系统库;Node 20 / 22 / 25 下都能运行,cli-wrapper.cjs也是。所谓 ABI 问题在当前版本已经不存在。 - 同版本重装:npm 显示的是
changed 2 packages(4.80s,再来一次 0.87s),而不是 up to date。
踩坑
**只隔离 HOME,挡不住全局 npm 的写入。**跑原生安装器时,我把 HOME 指向了 /tmp,以为这样就隔离了。结果安装器执行的 npm uninstall -g 用的是 npm 内置配置里的全局 prefix(/opt/homebrew),这个 prefix 和 HOME 无关——本机真实的 2.1.280 全局安装就这样被删掉了,claude 直接变成 command not found。之后用 npm install -g @anthropic-ai/claude-code@2.1.280 --registry https://registry.npmjs.org 恢复,约 3 秒。所以第 12 条的证据才会有三份:一份意外撞出来的 npm 调试日志(argv "uninstall" "--global" "@anthropic-ai/claude-code"),外加事后用假 npm 和 strings 做的安全复现。跑别人的安装器前,要么设 npm_config_prefix 指向隔离目录,要么把 npm 移出 PATH。
curl -f 挡不住地区拦截页。-f 只看 HTTP 状态码,302 跳转后的 200 HTML 它照单全收。curl … | bash 前先落盘看一眼。
**PATH 里的空格。**复现第 12 条时,第一次的 env PATH=$T/shim:$PATH … 因为 PATH 里有 VMware Fusion.app 这样带空格的目录而解析失败,exit 127——好在安装器根本没有执行。拼 PATH 时要加引号。
**把 --ignore-scripts 当成标题传给 npm run。**用 npm run term-shot … "--ignore-scripts:…" 出图时,npm 把以 -- 开头的标题当成了自己的参数吞掉了,两张图的标题都成了默认值。要写成 npm run term-shot -- …。