401 Unauthorized — 多半不是你刚换掉的那把 Key
- 确认端点要的是哪个头:ANTHROPIC_AUTH_TOKEN 发
Authorization: Bearer,ANTHROPIC_API_KEY 发x-api-key——设错了 Key 是好的也会 401。 env | grep -i anthropic看有没有旧值盖住新值,然后重启客户端(只在启动时读一次)。- 用 curl 发同样请求:curl 通了但客户端 401 = 客户端没读到你以为的变量;curl 也 401 = 凭证或端点的问题。
客户端连到兼容端点时,中间有两层各自独立的认证,401 可能来自其中任何一层,而两者的报错文本一模一样。重新生成自己的 Key 只处理得到其中一层——而且实际上那是比较少见的那一层。
你看到的报错
API Error: 401 {"type":"error","error":{"type":"authentication_error","message":"invalid x-api-key"}}
401 Unauthorized
Could not resolve authentication method. Expected either apiKey or authToken to be set.
{"error":{"message":"Invalid bearer token","type":"authentication_error"}}
四种都只表示「请求送到了,但服务器不接受它带的凭证」。没有一种会告诉你是哪一层拒绝的——但措辞可以缩小范围。
| 信息 | 它告诉你的事 | 最可能的原因 |
|---|---|---|
invalid x-api-key | 服务器读到了 x-api-key 头并拒绝了它的值 | Key 不对——或者端点要的是 Bearer token |
Invalid bearer token | 服务器读到了 Authorization: Bearer 并拒绝了它的值 | Token 不对——或者端点要的是 x-api-key |
Could not resolve authentication method | 客户端在发出任何东西之前就放弃了 | 两个变量都没设,或不是这个客户端读的那个 |
只有 401,没有内容 | 有东西拒绝了它,而且没有解释 | 你不知道它在路径上的某个代理或网关 |
两层认证
| 谁对谁认证 | 失败时的表现 | 谁能修 | |
|---|---|---|---|
| 第一层 | 你的客户端 → 端点 | 第一次请求就失败,每次都失败 | 你 |
| 第二层 | 端点 → 它的上游供应商 | 常是间歇性的,或在完全没改配置时突然开始 | 端点运营方 |
多数排错文章只写得到第一层——「检查你的 Key」。这建议没错,但它解释不了真正让人困惑的那种情况:一小时前还好好的,而你什么都没动。出现这种情况时,问题不在第一层。
按这个顺序排查
1 · 凭证真的进到请求里了吗#
复制粘贴会夹带看不见的东西——结尾换行、不换行空格、从聊天窗口带出来的弯引号。
printf '%s' "$ANTHROPIC_AUTH_TOKEN" | wc -c
printf '%s' "$ANTHROPIC_AUTH_TOKEN" | tail -c 20 | xxd | tail -2
字节数比看得见的长度多一,就是结尾跟着个换行一起被复制进来了。
2 · 你设的变量,产生的是端点要的那个头吗#
这是四种里最安静的一种失败,因为报错信息完全不会提示你。
| 变量 | 客户端发出的请求头 |
|---|---|
ANTHROPIC_API_KEY | x-api-key: <值> |
ANTHROPIC_AUTH_TOKEN | Authorization: Bearer <值> |
如果端点是靠 Authorization: Bearer 认证,而你设的是 ANTHROPIC_API_KEY,请求送达时根本没有 Authorization 头。服务器回 401,信息说 Key 无效。Key 是好的,错的是头。反过来也会发生。
接 9Coding 时设 ANTHROPIC_AUTH_TOKEN,Key 以 sk-9c- 开头。
3 · 你的 base URL 结尾,是客户端预期的位置吗#
客户端会自己在你给的 base URL 后面接上路径(/v1/messages、/v1/chat/completions)。如果你配的 base URL 结尾已经带了 /v1,请求就会打到 /v1/v1/messages。这通常回 404——但先认证再路由的网关会回 401。
echo "$ANTHROPIC_BASE_URL"
正确的值就是 https://api.9coding.com——结尾不要斜杠,不要带 /v1。
4 · 有没有旧值盖住了你刚设的#
写在 shell 配置文件里的值、写在项目配置文件里的值、以及当前这个 shell 里的值,是三件不同的事。客户端读的是其中一个,你改的可能是另一个。
env | grep -i anthropic
每一条都要看,不要只看你记得设过的那条。然后重启客户端——Claude Code 只在启动时读一次环境变量,长时间运行的进程会沿用它启动时的环境。
5 · 把客户端排除在外#
curl -s -w '\n%{http_code}\n' https://api.9coding.com/v1/models \
-H "Authorization: Bearer $ANTHROPIC_AUTH_TOKEN"
- curl 回 200、客户端回 401 → 客户端读的不是你以为的那个变量,回到第 4 步。
- curl 也回 401 → 问题在凭证或端点,不在客户端。
- curl 连状态码都拿不到 → 这不是认证问题,看连接错误。
什么时候这不是你的问题
以下四点同时成立,就是第二层的事,你这台机器上没有东西要修:
- 今天早些时候还能用,
- 你没有改任何配置,
- 指向同一个端点的其他客户端也是一样的错,
- 而且过一阵子会自己好。
在你自己的配置上再花一小时之前,先去看运营方的状态页与变更日志。一个既不公开状态页也不公开变更日志的端点,是你没办法排错的端点——这件事本身就值得知道。我们的在状态页。
提问题时请带上 request id——9Coding 的报错响应里都有一个,形如 (request id: 2026...)。这个 id 加上时间戳、模型名和完整报错文本,就能把那一次调用查出来。只说「用不了」的反馈没法排查。
401 和 403 不是同一个问题
403 表示请求通过了认证然后被拒绝:账号没有那个模型、那个地区或那个功能的权限。换 Key 保证没用。见 403 Forbidden。
相关报错
- 403 Forbidden — 通过认证但没有权限
- TLS 证书错误 — 为什么它第一次就失败且不会重试
- 连接错误 — 分辨网络、代理与端点
- 全部报错速查 — 排查手册总表