9Router 排坑实录:模型未找到、认证失败、连接超时一个都别漏
【免费下载链接】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
把 Claude Code、Codex、Cursor、Cline 这些 CLI 工具统一接到 9Router,本质上就是改两样东西:一个BASE_URL、一个 API Key,然后祈祷一次通过。但现实往往是——模型名照着文档抄了还是 404,Key 明明复制对了却报 401,请求偶尔能发出去、偶尔直接卡死超时。这类问题在 GitHub Issues 和社区教程里反复出现,几乎成了每个 9Router 新用户的第一道坎。
这篇文章不再停留在"重新连一下就好"的层面,而是直接钻进仓库源码,把模型未找到、认证失败、连接超时这三类高频故障的根因逐一拆开,并给出可复现的排查路径。
模型未找到:别名与模型列表对不上的真相
"Model not found"(404)是出现频率最高的报错。在 9Router 中,这个错误码来自统一错误映射表 errorConfig.js:上游返回 404 时,客户端收到的是{ type: "invalid_request_error", code: "model_not_found" }。也就是说,只要你请求的模型 ID 不在路由器的可解析范围内,就会得到这个结果。
根因一:模型 ID 的"别名前缀"没对上
9Router 的所有模型 ID 都带别名前缀,格式是别名/模型名,例如cc/claude-opus-4-6、kr/claude-sonnet-4.5、glm/glm-5。前缀不是随便写的,它来自 providers.js 中定义的PROVIDER_ID_TO_ALIAS映射,并在 models 路由 里通过ALIAS_TO_PROVIDER_ID反查、按outputAlias生成对外模型 ID。
这就带来第一个坑:同一个模型,在文档、Dashboard、CLI 工具三处的写法可能不同。源码里有一段专门的"前缀剥离"逻辑(见 models/route.js 第 496-509 行):从/v1/models拉回的模型 ID 如果已经带了outputAlias/、staticAlias/或providerId/前缀,会被逐一尝试剥掉再合并去重,最后统一重新拼成outputAlias/模型名。
如果你在 CLI 里配的模型名是claude-sonnet-4.5(漏了kr/),或者写成了kiro/claude-sonnet-4.5(真实前缀是kr/),路由器就找不到这条记录,直接 404。判断方法很简单:请求GET http://localhost:20128/v1/models,把响应里真实存在的 ID 原样复制进配置,不要凭记忆手敲。
根因二:模型列表是"动态拉取"的,文档只是基线
很多用户对着 README 里的模型清单配置,却发现kr/deepseek-3.2这种 ID 在GET /v1/models里根本不存在。这是因为 9Router 的模型列表是动态构建的:有活跃连接时,buildModelsList 会优先走LIVE_MODEL_RESOLVERS(Kiro、Qoder、Kimchi、GitHub Copilot、ClinePass、Grok CLI、Cursor、Zed 都在其中),用你的账号 Token 实时拉取上游目录;只有数据库不可用时才回退到PROVIDER_MODELS静态表。
resolveKiroModels这类解析器一旦失败(Token 过期、接口变更),代码会静默降级到静态列表——你看到的是"可用"列表,实际请求时却可能因为模型 ID 与上游不一致而 404。这也是为什么社区教程里反复强调"先跑一次 health check 和 model list 再下结论":/v1/models的实时输出才是唯一可信的模型清单,README 里的表格只代表基线能力。
根因三:Combo 座位写错或嵌套循环
9Router 的 Combo(cc/claude-opus-4-6 → glm/glm-5 → kr/claude-sonnet-4.5)会把整条链作为一个模型 ID 暴露。源码中 comboSeatLimits 对无斜杠的座位名会当作嵌套 Combo 递归解析,且带了循环守卫(visiting集合)。如果你在 Combo 里把某个座位名写成了不存在的别名,聚合能力计算(aggregateComboCapabilities)虽然不会崩,但发布出去的 ID 和实际可路由的座位对不上,同样会 404。
排查清单:
GET /v1/models核对真实 ID,禁止凭文档手写;- 确认别名前缀(
cc/、cx/、gh/、cu/、glm/、kr/、oc/等),大小写敏感; - Combo 座位逐个验证存在性,避免嵌套死循环;
- 若用了自定义模型,检查
providerAlias是否正确——aliasRepo.js中自定义模型的 key 是providerAlias|id|type,前缀不一致同样查不到。
认证失败:Key 配置的五个易错点
401authentication_error的语义是"这个 Key 不被接受"。但 9Router 的认证链路分两层:外层是"9Router 自己的 Key",内层是"上游提供商的 Key/Token"。两层混在一起,是绝大多数认证排查走弯路的原因。
易错点 1:把sk_9router当成了真实 Key
这是最隐蔽的坑。UI 在未配置云 Key 时默认展示占位符sk_9router(见 DefaultToolCard.js/dashboard/cli-tools/components/DefaultToolCard.js) 第 23 行)。但源码注释写得很清楚:这个占位符从来不是真实 Key。resolveApiKey.js 专门做了修正——过去 CLI 配置路由会把这个占位符写进Authorization头,导致所有开启requireApiKey的部署一律 401(对应 issue #4399),现在解析逻辑明确"占位符永不写入"。
所以:Dashboard 里显示的sk_9router只是示例,真正的 Key 要在 Endpoint 页面的 API Keys 卡片里创建,或直接复用 Dashboard 登录凭证。
易错点 2:requireApiKey开启后,Key 校验是"可选即 401"
9Router 默认REQUIRE_API_KEY=false,/v1/*路由不做 Bearer 校验;但一旦你在 Endpoint 设置里打开 "Require API key"(生产环境强烈建议),校验就变成硬性要求。v1beta/models 路由 展示了统一模式:settings.requireApiKey为真时,缺失 Key 直接回 401 "Missing API key",无效 Key 回 401 "Invalid API key"。
注意:Dashboard 登录密码和 API Key 是两套体系。CLI 工具用的是 API Key,Dashboard 用的是INITIAL_PASSWORD(默认123456)。拿登录密码当 API Key 填,必然 401。
易错点 3:上游 Key 过期 vs 外层 Key 错误,要分清楚
外层 Key 正确的情况下,401 往往来自上游。错误映射表里 errorConfig.js 对 401 统一归类为invalid_api_key,但实际来源可能是:
- Kiro/Perplexity 等"cookie/Token 型"提供商:凭证过期后,executor 层会明确提示"re-paste your SSO cookie"(如 grok-web.js、perplexity-web.js);
- Qoder 无用户 ID 时无法签名,qoder.js 会主动回 401 以便 Dashboard 提示重新连接;
- OAuth 型提供商 Token 过期:9Router 有自动刷新机制(
tokenRefresh),刷新失败时表现为间歇性 401。
判断技巧:看错误消息文本。外层 Key 问题消息是 "Invalid API key provided" / "Missing API key";上游问题往往带着提供商名("Grok auth failed"、"Perplexity auth failed")或提示重新连接。
易错点 4:Bearer 头与 x-api-key 头混用
9Router 兼容 OpenAI 和 Anthropic 两类客户端。OpenAI 系走Authorization: Bearer <key>,Anthropic 系(Claude Code)走x-api-key+anthropic-version头——这在 models/route.js 的fetchCompatibleModelIds里体现得很清楚:Anthropic 兼容提供商同时带x-api-key和Authorization,并且自动把/messages路径改写为/models再探测。
如果客户端是 Claude Code 但你在配置里手动填了Authorization: Bearer,或者反过来,上游探测就可能失败。正确姿势是让 CLI 工具使用各自的官方配置方式(Claude Code 用ANTHROPIC_BASE_URL+ANTHROPIC_AUTH_TOKEN),让 9Router 自己处理头格式。
易错点 5:多实例互联时的递归环
9Router 支持实例互相连接(A 实例把 B 实例当上游)。源码里专门定义了内部头x-9r-internal-models-fetch(models/route.js 第 168 行),用于识别跨实例的/models抓取并打断递归环。如果两台实例互指且没有这个保护,模型探测会无限循环。排查时如果发现fetchCompatibleModelIds反复触发且响应缓慢,先检查是否存在循环连接。
排查清单:
- 在 Dashboard 创建真实 API Key,替换
sk_9router占位符; - 确认
requireApiKey状态与你的客户端行为一致; - 区分错误消息来源(外层 vs 上游),上游过期去 Dashboard 重新连接;
- OpenAI 系用
Bearer,Claude Code 用官方环境变量; - 检查多实例互联是否成环。
连接超时与网络层的排查路径
超时问题在 9Router 里分三个层面:客户端到 9Router、9Router 到上游、上游自身的可用性。三者症状相似(卡住、超时、502/504),但排查路径完全不同。
超时可能在 9Router 内部:上游探测有 5 秒硬超时
models/route.js 的fetchCompatibleModelIds给上游/models探测挂了AbortController+ 5 秒超时,失败静默返回空数组。也就是说:如果上游慢,GET /v1/models会"成功但缺模型",而不是报错。你看到的现象是"模型列表忽多忽少",本质是上游探测超时后降级到了静态表。
代理配置是超时的头号来源
9Router 的出站请求统一走 proxyFetch.js,代理解析优先级明确:HTTPS_PROXY > ALL_PROXY(https 目标)、HTTP_PROXY > ALL_PROXY(http 目标),并支持大小写变体。以下几个坑是社区反馈里最集中的:
NO_PROXY写错导致该直连的被代理:shouldBypassByNoProxy的匹配规则是hostname === pattern || hostname.endsWith(".pattern"),且支持*通配。如果你把NO_PROXY写成localhost而目标是127.0.0.1,匹配不上,请求就会被错误地送进代理;- 代理 URL 忘记协议:
normalizeProxyUrl会宽容地把127.0.0.1:7890自动补成http://127.0.0.1:7890,但如果你填的地址本身不可达,表现就是持续超时; - 企业 MITM 证书导致 TLS 握手失败:
fetchWithTlsFallback在非STRICT_SSL模式下遇到证书错误会自动用rejectUnauthorized: false重试——如果你把STRICT_SSL设成了true,这个兜底被关闭,自签名证书场景直接握手失败。
端口与监听地址问题
9Router 默认端口是20128。README 的 Troubleshooting 明确提示:Dashboard 打开在错误端口时,需要同时设置PORT=20128和NEXT_PUBLIC_BASE_URL=http://localhost:20128。如果NEXT_PUBLIC_BASE_URL与PORT不一致,CLI 客户端拿到的回调地址就是错的,表现同样接近"连不上"。
另外,localhost与127.0.0.1在某些环境(IPv6 优先、防火墙策略)下行为不同。CLI 配置端点时建议统一用http://127.0.0.1:20128,避免 DNS/回环解析差异。
把"连接超时"当作诊断信号,而不是终点
在 9Router 的错误分类里(errorConfig.js),502/504 都被标记为server_error类,并配有"upstream provider error"的默认消息。这意味着:当 CLI 报超时/网关错误时,问题大概率不在 9Router 本体,而在上游链路。此时应逐层验证:
curl http://127.0.0.1:20128/v1/models确认 9Router 本身活着;- 检查上游提供商状态与配额(Dashboard 的 Quota 追踪器);
- 确认
HTTP_PROXY/HTTPS_PROXY/NO_PROXY环境变量是否符合预期; - 开启
ENABLE_REQUEST_LOGS=true,在logs/目录里看请求实际发出去了没有、上游响应了什么; - 确认
requireApiKey等安全设置没有挡住 CLI 的请求(401 与超时在客户端表现可能混淆)。
一张图看懂 9Router 的请求链路
9Router 的完整架构可以从 README 的架构图直观理解:CLI 工具请求打到http://localhost:20128/v1,9Router 完成 RTK 压缩、格式翻译(OpenAI ↔ Claude)、配额追踪后,按"订阅 → 便宜 → 免费"三级回退路由到上游。这张图解释了为什么同一个请求在不同时刻可能命中完全不同的上游——模型未找到、认证失败、超时,都可能在回退切换的瞬间出现:
排坑的核心心法,就是时刻记住这条链路的每一跳都有独立的"身份"与"网络":模型 ID 要匹配 9Router 的别名空间,Key 要区分外层/上游两层,超时要先定位是哪个环节掉链子。把这三件事拆开,绝大多数 404、401、超时问题都能在五分钟内定位到根因。
结语
模型未找到、认证失败、连接超时,表面上是三类孤立的报错,实际上共享同一个底层逻辑:9Router 是一个"翻译 + 路由"代理,不是简单的端口转发。它的模型命名空间、双层认证、动态模型探测、代理链路,都意味着你不能用"直连 OpenAI"的思维去排查。记住三个铁律:以/v1/models的真实输出为准、以错误消息的文本区分认证层级、以请求日志定位超时环节——这三条能覆盖社区反馈中绝大多数的翻车场景。剩下的,就是让自动回退和 RTK 帮你把编码节奏拉满。
【免费下载链接】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),仅供参考