1. SSH 连上 Mac 后 ClaudeCode 报 Invalid API key 的真实场景
你人在另一台电脑上,SSH 连回家里那台 Mac,敲下claude想让它帮忙改点代码,结果终端直接甩你一句Invalid API key · Please run /login。你明明在 Mac 本机上用得好好的,怎么一远程就翻脸?这个场景我遇到过不止一次,问题基本不在 ClaudeCode 本身,而在「SSH 会话没有继承你本机那套登录态和配置」。
先把概念捋清楚。ClaudeCode 是 Anthropic 出的命令行编码工具,跑在终端里,能读你当前目录的代码、执行命令、改文件。它要调用模型,就得有凭证。凭证从哪来?两条路:一条是登录态(OAuth token,存在 macOS Keychain 或配置目录里),另一条是环境变量或 settings 文件里写死的 API key。你在 Mac 本机开终端,图形登录会话已经把 Keychain 解锁了,ClaudeCode 能直接读到 token,所以不报错。但 SSH 进来的是一个非交互式登录 shell,Keychain 默认锁着,环境变量也没加载,ClaudeCode 找不到任何有效凭证,就报Invalid API key。
这里有个常见误解:很多人以为报Invalid API key就是 key 写错了。其实在 SSH 场景下,更大概率是「压根没读到 key」,而不是「key 不对」。这两者的排查方向完全不同。前者查加载链路,后者查 key 本身。你要先分清自己属于哪种。
适合读这篇的人:用 Mac 做开发机、经常 SSH 远程操作、想用 ClaudeCode 但被登录问题卡住的同学。如果你是把 ClaudeCode 装在 Linux 服务器上,思路类似但路径不同,本文以 Mac 为主。
我试过的排查顺序是:先看环境变量有没有传进来,再看 settings 文件路径对不对,最后看登录态在 SSH 会话里能不能读到。三条链路逐项过,基本能定位。下面按这个顺序展开,每一步都给可复制的命令和配置。
2. TaoToken 前置准备:把凭证来源固定下来
在排查之前,建议先把「凭证从哪来」这件事固定死,别让它一会儿读 Keychain、一会儿读环境变量,那样排查起来更乱。我的做法是统一走 settings 文件 + 环境变量,把模型请求指向 TaoToken 的兼容接口,这样 SSH 会话只要加载了环境变量就能工作,不依赖 Keychain 解锁。
TaoToken 是一个模型 API 聚合服务,提供 Anthropic 兼容的接口地址,ClaudeCode 这类工具可以直接把 Base URL 指过去。官网在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 入口是 https://taotoken.net/api 。你需要先去控制台拿一个 API Key,地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,Key 的创建页在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。
拿到 Key 之后,先别急着往 ClaudeCode 里塞。你要理解 ClaudeCode 读配置的优先级,否则改了文件不生效会怀疑人生。大致优先级是:进程环境变量 > 项目级 settings > 用户级 settings > 默认登录态。也就是说,如果你在 shell 里export ANTHROPIC_API_KEY=xxx,它会盖过 settings 文件里的值。SSH 会话里环境变量往往没加载,所以实际生效的是 settings 文件或登录态。
这里要提醒一句:不要把 Key 硬编码进会提交到 git 的文件里。用户级 settings 放在~/.claude/下,相对安全;项目级.claude/settings.json如果进了仓库,Key 就泄露了。SSH 场景我推荐用用户级配置 + shell 环境变量兜底。
关于模型 ID,TaoToken 的接口兼容 Anthropic 格式,ClaudeCode 里配置的模型名要和你账号可用的模型对应。你可以在模型对话页 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=chat&utm_campaign=rewrite 先确认能正常对话,再往 CLI 里配。如果你打算长期用 ClaudeCode 做编码或跑 Agent,可以考虑 Coding Plan,入口在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,比按量调用更适合高频场景。
前置准备的核心就三件事:拿到 Key、确定 Base URL、想清楚配置放哪一层。这三件定下来,后面的排查才有基准。
3. 可复制配置:settings 文件与环境变量怎么写
这一节给可直接复制的片段。先说用户级 settings 文件,路径是~/.claude/settings.json。如果目录不存在,先建:
mkdir -p ~/.claude然后写入配置。注意 JSON 格式,别多逗号:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_API_KEY": "sk-你的TaoToken密钥", "ANTHROPIC_MODEL": "claude-sonnet-4-20250514" } }这个文件的作用是:ClaudeCode 启动时会读取env字段,把它注入到自己的运行环境里。这样即使 SSH 会话的 shell 没 export 这些变量,ClaudeCode 自己也能拿到。这是解决 SSH 场景最直接的一招。
但要注意,ANTHROPIC_MODEL的值要换成你账号实际可用的模型 ID。写错了会报模型不存在,而不是 API key 错误,别混淆。
如果你更习惯用 shell 环境变量,可以在~/.zprofile或~/.zshrc里加:
export ANTHROPIC_BASE_URL="https://taotoken.net/api" export ANTHROPIC_API_KEY="sk-你的TaoToken密钥" export ANTHROPIC_MODEL="claude-sonnet-4-20250514"SSH 非交互式登录默认读~/.zprofile,不读~/.zshrc(后者是交互式 shell 才读)。所以如果你只在.zshrc里写了 export,SSH 进来跑claude可能读不到。这是很多人踩的坑:本机终端能用,SSH 就不行,因为本机开的是交互式 shell。
如果你用的是 Codex 那套,配置在~/.codex/auth.json,格式不同,但思路一样:Base URL、Key、Model ID 三件套要齐全。Cline 走 MCP 的话,配置在 MCP server 的 env 里,也是这三样。CC Switch 这类切换工具,本质是帮你改这些文件,理解底层就不容易被工具绕晕。
再强调一次三件套:Base URL 填https://taotoken.net/api,Key 填你创建的,Model ID 填账号可用的。缺任何一个都会报错,但报错信息不一样,下一节教你怎么区分。
改完文件后,SSH 会话里执行source ~/.zprofile让它生效,或者直接重连一次 SSH。
4. 验证请求:SSH 会话下确认配置真的加载了
配置写完不代表生效,必须验证。SSH 进来后,按顺序跑这几条命令。
先确认环境变量在不在:
echo $ANTHROPIC_BASE_URL echo $ANTHROPIC_API_KEY | head -c 8 echo $ANTHROPIC_MODEL第一条应该输出https://taotoken.net/api。第二条只打印 Key 的前 8 位,确认非空即可,别把完整 Key 打到屏幕上。第三条输出模型 ID。如果前两条是空的,说明你的 shell 没加载配置,回到上一节检查.zprofile。
再确认 ClaudeCode 自己读到的配置。ClaudeCode 有诊断命令,可以看它当前认为的配置:
claude --version claude config list不同版本命令略有差异,如果config list不存在,直接看它启动时的输出。重点是确认它读到的 Base URL 是 TaoToken 的地址,而不是默认的 Anthropic 官方地址。
然后做一次最小请求验证。最直接的是让 ClaudeCode 跑一个不需要改文件的简单任务:
claude -p "回复 ok 两个字"-p是 print 模式,跑完就退出,适合验证。如果返回ok,说明整条链路通了:环境变量加载 → settings 生效 → 请求打到 TaoToken → 模型返回。如果还是报Invalid API key,说明凭证没读到;如果报连接错误,说明 Base URL 或网络有问题。
你也可以绕过 ClaudeCode,直接用 curl 验证 Key 本身是否有效:
curl -s https://taotoken.net/api/v1/messages \ -H "x-api-key: $ANTHROPIC_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{"model":"claude-sonnet-4-20250514","max_tokens":16,"messages":[{"role":"user","content":"hi"}]}'如果这条 curl 返回正常 JSON,说明 Key 和 Base URL 都没问题,问题在 ClaudeCode 的配置加载;如果 curl 也报 401,说明 Key 本身有问题,去控制台重新确认。这一步能把「Key 错」和「配置没加载」彻底分开,非常关键。
验证通过后,你可以在 SSH 会话里正常用 ClaudeCode 了。如果长时间挂着 tmux,注意 Keychain 会话可能超时,但因为我们走的是 settings 文件里的 Key,不依赖 Keychain,所以不受影响。这也是我推荐用 API key 而非登录态的原因之一。
5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth
这一节对照真实报错逐条拆。你遇到的报错信息不同,排查方向也不同。
报401 Invalid API key或authentication_error:这是最典型的。先跑上一节的 curl,如果 curl 也 401,说明 Key 无效或写错。检查三点:Key 有没有多余空格、有没有被 shell 转义、是不是复制时漏了字符。如果 curl 正常但 ClaudeCode 报 401,说明 ClaudeCode 没读到你的 Key,读的是旧的登录态或空值。这时候检查~/.claude/settings.json的 JSON 是否合法,用python -m json.tool ~/.claude/settings.json验证格式。JSON 里多一个逗号就会导致整个文件被忽略,ClaudeCode 静默回退到登录态,然后报 401。
报local proxy failed或连接被拒:这通常不是 Key 问题,而是 Base URL 或网络层。确认ANTHROPIC_BASE_URL是https://taotoken.net/api,没有多余路径,没有尾部斜杠。有些教程让你填/v1,但 ClaudeCode 会自己拼路径,填多了会 404。另外检查 SSH 会话里有没有残留的代理环境变量,env | grep -i proxy看一下,有的话 unset 掉。
报reading choices或响应解析失败:这类错误说明请求发出去了,但返回格式不对。常见原因是 Base URL 指向了一个不兼容 Anthropic 格式的端点,或者模型 ID 写错导致返回了错误结构。确认你用的是 TaoToken 的 Anthropic 兼容入口,模型 ID 和账号可用列表一致。可以在模型对话页先确认模型能正常返回,再回 CLI 排查。
报Please run /login或 OAuth 相关错误:这是 ClaudeCode 在提示你走登录流程。如果你已经配了 API key,它不该再提示登录。出现这个说明它没读到你的 key,回退到了 OAuth 模式。检查环境变量优先级:如果你在 shell 里 export 了ANTHROPIC_API_KEY,但值是空的,它会覆盖 settings 里的有效值。用echo $ANTHROPIC_API_KEY确认非空。另外,某些版本的 ClaudeCode 会缓存登录态,可以尝试清掉~/.claude/下的缓存文件后重试。
改了配置不生效:SSH 会话是独立进程,改完文件要重新加载或重连。source ~/.zprofile只对当前 shell 生效,如果你在 tmux 里,每个 pane 都要重新 source。最稳的办法是退出 SSH 重连一次。
Keychain 反复弹窗或超时:如果你坚持用登录态而非 API key,SSH 场景下 Keychain 默认锁着,需要在.zprofile里加解锁逻辑。但更简单的做法是直接用 API key,绕开 Keychain。这也是本文推荐 settings 文件方案的原因。
排查的核心逻辑:先用 curl 确认 Key 和端点本身没问题,再确认 ClaudeCode 读到的配置和你写的一致,最后确认 SSH 会话的环境变量没有覆盖或污染。三步走完,基本没有定位不了的问题。
6. 把配置固定下来,长期稳定用 ClaudeCode
排查完一次,别让它下次再犯。我的做法是把配置固化:用户级 settings 文件写死 Base URL、Key、Model ID 三件套,.zprofile里只做兜底 export,项目级配置不碰凭证。这样无论本机还是 SSH,读到的都是同一套配置,行为一致。
如果你经常在多台机器之间切换,可以把~/.claude/settings.json的内容做成模板,新机器上复制过去改 Key 就行。注意别把带 Key 的文件同步到公开仓库。
对于长期高频使用 ClaudeCode 跑编码任务的场景,按量调用可能不如 Coding Plan 划算,入口在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 。如果你只是想验证模型是否正常,用模型对话页 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=chat&utm_campaign=rewrite 更快。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,遇到接口细节可以查。ClaudeCode 相关的接入说明在 https://taotoken.net/ClaudeCodeAnthropic?utm_source=taotoken_aicg_blog_end&utm_content=ClaudeCodeAnthropic&utm_campaign=rewrite 。
最后留一个实用习惯:每次改完配置,先跑claude -p "回复 ok"验证,通过了再干正事。这个命令两秒钟,能帮你省掉半小时的排查。SSH 场景下尤其值得,因为环境差异比本机大,早验证早安心。