把 Claude Code 接入 9Router 后我踩的 5 个坑

发布时间:2026/10/10 11:03:11
把 Claude Code 接入 9Router 后我踩的 5 个坑 把 Claude Code 接入 9Router 后我踩的 5 个坑【免费下载链接】9routerUnlimited FREE AI coding. Connect Claude Code, Codex, Cursor, Cline, Copilot, Antigravity to FREE Claude/GPT/Gemini via 40 providers. Auto-fallback, RTK -40% tokens, never hit limits.项目地址: https://gitcode.com/GitHub_Trending/9r/9router9Router 是一个跑在本地默认http://localhost:20128的 AI 路由网关它对外只暴露一套兼容 OpenAI 与 Anthropic 协议的端点对内把请求翻译、路由、降级到 40 上游 Provider并且能自动在「订阅额度 → 低价模型 → 免费模型」之间做账户级与模型级故障转移。对 Claude Code 用户来说接入看起来只需要一行环境变量ANTHROPIC_BASE_URLhttp://localhost:20128/v1但真正把日常开发切过去之后我接连踩了 5 个坑——有些是配置层面的低级失误有些则必须翻进源码才能定位。这篇文章把每个坑的现场、根因和源码依据都摊开讲清楚希望你能一次绕开。在展开之前先理解请求在 9Router 内部是怎么走的Claude Code 发出/v1/messages请求 → 9Router 的兼容 API 层src/app/api/v1/messages/route.js→ 会话编排层chat.js解析模型字符串、解析别名、选 Provider 与账号 → 核心执行层chatCore.js做协议翻译与流式转发 → 上游 Provider。架构文档 里的请求生命周期时序图把这条链路画得很清楚。这意味着任何一个环节的「小偏差」——少一个后缀、多一个前缀、错一个版本号——都会在整条链路上被放大成「诡异的故障现场」。下面就是这 5 个坑。坑一环境变量没生效的诡异现场第一个坑的现场最经典在~/.zshrc里写了export ANTHROPIC_BASE_URLhttp://localhost:20128/v1echo $ANTHROPIC_BASE_URL也输出正确但claude一启动还是直连 Anthropic 官方配额照扣。根因之一手写 Base URL 时丢了/v1后缀。9Router 的兼容 API 路径是http://host:20128/v1而 Dashboard 和 CLI 工具在写配置时会自动补全后缀——claude-settings 配置路由 里有这样一段// Normalize ANTHROPIC_BASE_URL to ensure /v1 suffix if (env.ANTHROPIC_BASE_URL) { env.ANTHROPIC_BASE_URL env.ANTHROPIC_BASE_URL.endsWith(/v1) ? env.ANTHROPIC_BASE_URL : ${env.ANTHROPIC_BASE_URL}/v1; }connectTools.js 里的v1()辅助函数也做了同样的兜底。问题恰恰出在「有兜底」这件事上通过 9Router 界面点「Apply」永远不会踩到这个坑所以你根本不会意识到——一旦脱离界面、手写环境变量写http://localhost:20128不带/v1就会 404而 404 在 Claude Code 里的表现往往是模糊的连接错误不会提示你「路径写错了」。根因之二shell 环境变量与~/.claude/settings.json的env块互相覆盖。Claude Code 同时读两处配置进程环境变量和~/.claude/settings.json中的env对象。如果你之前配过别的网关settings.json 里残留着旧的ANTHROPIC_BASE_URLshell 里新 export 的值会被它覆盖或与之冲突——这正是「改了环境变量却不生效」最常见的解释。9Router 的集成文档Claude Code 集成和 Dashboard 都会写到~/.claude/settings.json排查时优先看这个文件而不是反复sourceshell 配置。根因之三改了 shell 配置忘了source或者用sudo claude启动导致环境变量丢失。前者是低级失误后者则是 sudo 重置环境的经典行为。正确的验证姿势也写在了集成文档里echo $ANTHROPIC_BASE_URL curl http://localhost:20128/health # 确认网关活着 cat ~/.claude/settings.json # 确认 env 块里没有旧值坑二别名映射写错的连锁反应第二个坑比第一个隐蔽得多。Claude Code 支持--model sonnet这类别名也支持--model cc/claude-sonnet-5这类带 Provider 前缀的完整模型 id。我把 Dashboard 上的别名映射从「cc/claude-sonnet-5」改成「claude-sonnet-5」之后同样的 prompt 开始频繁 403、报 model not found一度以为是免费 Provider 额度问题。根源在模型字符串的解析规则。模型解析核心 的parseModel把请求里的模型字符串分成两类含/的走「Provider 别名 / 模型 id」路径不含/的走「模型别名」路径。前者直接把前缀解析成 Provider后者才去查别名表查不到就退回inferProviderFromModelName按模型名前缀猜 Provider[/^claude-/, anthropic],也就是说claude-sonnet-5无前缀会被推断到anthropicProvider——这是走API Key的那条链路而cc/claude-sonnet-5里的cc是 Claude Code OAuth Provider 的注册别名见 Claude Code 注册表 里的alias: cc。两者凭证体系完全不同一个用你的订阅 OAuth 会话一个用独立的 Anthropic API Key。别名映射少写一个cc/请求就从「订阅额度」被悄悄挪到了「API Key 计费」甚至因为该 Key 无权限直接 401/403——这就是「写错一个字符的连锁反应」。还有两个更隐蔽的优先级问题。在 本地模型解析 的getModelInfo里解析顺序是先查combo 名称再查模型别名。如果你给某个 Combo 起的名字恰好叫sonnet别名映射写得再对也不会生效。同理RESERVED_PROVIDER_PREFIXES机制把内置 Provider 的 id/alias 全部列为保留前缀自定义 Provider Node 不能占用这些前缀——别把自定义节点前缀起成cc、openai这类名字。最后的版本陷阱在默认模型 id 上。CLI 工具常量 里定义了四个别名环境变量及默认值别名环境变量默认模型 idfableANTHROPIC_DEFAULT_FABLE_MODELcc/claude-fable-5opusANTHROPIC_DEFAULT_OPUS_MODELcc/claude-opus-5sonnetANTHROPIC_DEFAULT_SONNET_MODELcc/claude-sonnet-5haikuANTHROPIC_DEFAULT_HAIKU_MODELcc/claude-haiku-4-5-20251001模型 id 普遍带日期后缀如claude-haiku-4-5-20251001如果从网上抄到过期的旧 id 硬编码进去结果就是安静的 model not found。我的教训是别名只做 UI 层的便捷映射/api/models/alias真正写进环境变量的一律用带cc/前缀的完整 id。坑三权限问题藏在文件写入的细节里第三个坑来自 9Router 写配置文件的姿势。在 connectTools.js 里能看到两个值得警惕的设计// Files hold the API key → owner-only (0600) on POSIX; no-op on Windows. const SECRET_MODE 0o600; // One-time backup of the users pre-9router file, then write. function writeFile(file, content) { const backup ${file}.bak-9router; if (fs.existsSync(file) !fs.existsSync(backup)) { fs.copyFileSync(file, backup); ...配置写入~/.claude/settings.json时会强制0600权限并先生成.bak-9router备份。副作用是一旦你用sudo跑过 9router或者手动chmod过这个文件settings.json的属主和权限就可能变得不对——普通用户身份启动的 Claude Code 要么报 permission denied要么直接静默忽略该文件表现又是「配置没生效」。这类问题在文件系统层面任何日志都查不出来只能ls -l ~/.claude/settings.json看属主。另外~/.claude.jsonClaude Code 读 MCP 配置的地方注意不是 settings.json也有同样的写入路径claude-settings 配置路由 里写得很明确。权限坑的另一面是默认口令。架构文档 的安全边界一节明确指出INITIAL_PASSWORD默认值是123456JWT_SECRET不设置则退回默认实现。如果你把 9Router 绑定到0.0.0.0或者开了云端 Endpoint 却忘改默认口令相当于把本地网关的管理面裸奔在网络上。接入前先改掉这两个默认值比接完后排查任何 5xx 都重要。坑四版本不对齐的玄学第四个坑是「版本玄学」同样的配置换了一台机器Claude Code 版本不同就出问题。根源在 9Router 上游请求的伪装头。看 providers/shared.jsexport const CLAUDE_CLI_VERSION 2.1.280;9Router 向上游发请求时会把 User-Agent 伪装成claude-cli/2.1.280并在 Claude Code 注册表 里固定发送一长串Anthropic-Beta头claude-code-20250219、interleaved-thinking-2025-05-14、token-efficient-tools-2026-03-28等等。这意味着9Router 模拟的是某个特定版本的 Claude Code 客户端行为。本地装的 Claude Code 版本如果明显偏旧或偏新它在客户端侧开启的功能开关比如新的 beta 能力、工具调用格式与 9Router 固定模拟的版本不一致就会出现「同一模型在 A 机器正常、在 B 机器抽风」的玄学问题。更值得留意的是同一个注册表文件里赫然写着display: { deprecated: true, deprecationNotice: RISK_NOTICE, },Claude Code OAuth 直连被 9Router 官方标记为 deprecated 风险提示——上游协议变动频繁这类 Provider 的稳定性天然依赖版本对齐。所以我后来养成的习惯是固定 Claude Code 与 9Router 的版本组合不轻易升任何一方升级后先在curl -X POST http://localhost:20128/v1/messages上做一次最小请求冒烟测试确认 UA 与 beta 头没有导致上游拒绝。坑五日志盲区最后一个坑也是最浪费时间的一个出问题想查 9Router 的请求日志打开logs/目录发现是空的。翻源码才找到原因——请求日志模块 在模块加载时读一次环境变量const LOGGING_ENABLED typeof process ! undefined process.env?.ENABLE_REQUEST_LOGS true;ENABLE_REQUEST_LOGStrue必须在启动 9Router 进程之前设置。运行中再 export 无效因为createRequestLogger会直接返回createNoOpLogger()这个全空实现——所有logClientRawRequest、logTargetRequest、appendProviderChunk都是 no-op。而且日志目录是相对process.cwd()的logs/从不同目录启动 9Router日志落点也不一样。真正开启后这套日志其实非常强每次请求会生成一个以{sourceFormat}_{targetFormat}_{model}_{timestamp}命名的会话目录内部按 7 个阶段落盘——1_req_client.json客户端原始请求、3_req_openai.jsonOpenAI 中间格式、4_req_target.json发给上游的最终请求、5_res_provider.txt上游流式响应、7_res_client.txt回给客户端的流——几乎能完整还原一次「Claude Code → 9Router → 上游」的往返。定位别名路由错误、格式翻译问题都靠它。但这里还有一个反向盲区需要警惕脱敏代码被注释掉了。maskSensitiveHeaders里保留了「keep full token for testing」的注释也就是说开启请求日志后请求头含 Authorization / x-api-key会以明文写进logs/目录。架构文档 里明确提示「Request logger writes full headers/body when enabled; treat log directory as sensitive」。排查完问题记得清理或加密这个目录别把它随手上传到任何远端。最后补充一个不依赖ENABLE_REQUEST_LOGS的排障入口~/.9router/log.txt记录了逐请求的状态行usage.json记录用量明细见 架构文档 的 Observability 一节。遇到「额度没了但不知道花在哪」的问题这两个文件是第一时间该看的地方——它们不需要任何环境变量永远在写。小结一份踩坑清单把这 5 个坑压缩成一条自查清单下次接入时对着过一遍ANTHROPIC_BASE_URL必须以/v1结尾且~/.claude/settings.json的env块里没有残留旧值模型一律用cc/前缀的完整 id如cc/claude-sonnet-5别名只做 UI 层映射Combo 命名避开别名检查~/.claude/settings.json属主与权限0600 属主必须是当前用户改掉默认INITIAL_PASSWORD固定 Claude Code 与 9Router 的版本组合升级后先冒烟测试再切流量需要排查时启动前就设好ENABLE_REQUEST_LOGStrue用完立即清理含明文 token 的logs/目录。配置本身只有一行环境变量但网关两侧的隐式约定远比看上去多。希望这份源码级的复盘能让你少走这 5 段弯路。【免费下载链接】9routerUnlimited FREE AI coding. Connect Claude Code, Codex, Cursor, Cline, Copilot, Antigravity to FREE Claude/GPT/Gemini via 40 providers. Auto-fallback, RTK -40% tokens, never hit limits.项目地址: https://gitcode.com/GitHub_Trending/9r/9router创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询