9Router 故障排查完全指南:配额、限流、OAuth 与连接问题的一线实战手册
【免费下载链接】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
9Router 是一个把 Claude Code、Codex、Cursor、Cline、Copilot 等编码工具统一接入 40+ 免费/付费模型提供商的本地网关,其核心价值在于通过 Combo 回退链实现"订阅优先、便宜兜底、免费救急"的路由策略。本文以官方 troubleshooting.md(西班牙语版,与 英文版 内容一致)为主线,逐项拆解 8 类高频报错(空响应、限流、Token 过期、成本失控、连接拒绝、Dashboard 打不开、模型找不到、响应慢、API Key 无效),并深入仓库源码验证每一项的底层实现原理。读完本文,你将能在 5 分钟内定位 9Router 日常使用中的绝大多数故障,并掌握"组合回退 + 配额追踪 + Token 自愈"这套开箱即用的自救体系。
0. 前置知识:9Router 的两个端口与三类端点
排查前先记住 9Router 的网络布局,后续所有诊断命令都围绕它们展开:
| 端点 | 地址 | 用途 |
|---|---|---|
| Dashboard | http://localhost:3000 | 图形化管理界面(提供商、Combo、配额、用量统计) |
| OpenAI 兼容网关 | http://localhost:20128/v1 | 供 Cursor、Cline、Codex 等工具接入的 API 端点 |
| 内部管理 API | http://localhost:20128/api/* | CLI 与 Dashboard 调用的管理接口 |
端口20128与http://localhost:20128/v1在 README.md 中作为标准接入方式反复出现;CLI 客户端的默认配置也是host: "localhost", port: 20128(见 cli/src/cli/api/client.js)。Dashboard 则默认运行在http://localhost:3000。记住"网关看 20128、界面看 3000"这条准则,以下所有排查思路都会更快落地。
1. "Language model did not provide messages":空响应与配额耗尽
1.1 症状与成因
症状:请求返回空响应或报错(典型如 Claude 系工具提示模型没有返回任何消息内容)。
官方列出的三类成因:
- 提供商配额已耗尽(subscription quota exhausted);
- API key 无效或已过期;
- 模型当前不可用。
其中配额耗尽是最常见原因——订阅类提供商(Claude Code、Codex)普遍采用 5 小时滚动窗口配额,免费提供商则常带每日请求数或每月 token 上限。
1.2 标准处置流程
- 查看配额状态:
Dashboard → Providers → 查看配额追踪器,若配额耗尽,等待重置或切换提供商; - 配置 Combo 回退链:
Dashboard → Combos → 创建回退链,例如cc/claude-opus → glm/glm-4.7 → if/kimi-k2(订阅 → 便宜 → 免费); - 验证提供商连接:
Dashboard → Providers → 必要时重新连接。
1.3 源码纵深:Combo 回退链是如何"接力"的
Combo 回退不是简单的失败重试,而是按顺序逐个尝试、遇错即换的串行路由。核心实现在 open-sse/services/combo.js 的handleComboChat:
- 依次调用
handleSingleModel(body, modelStr),只要返回 2xx 成功即立即返回结果(result.ok判断,见 combo.js); - 失败时解析错误体(
error.message、retryAfter),交给checkFallbackError判定是否应回退到下一个模型; - 对 503/502/504 这类瞬时错误,会先等待冷却(cooldown)再继续,避免"一抖就跳"(见 combo.js);
- 全部模型失败时返回 503,并附带最早的重试时间(
formatRetryAfter),让客户端可以精确等待。
这也解释了官方建议"在 Combo 末尾永远挂一个免费档"(如if/kimi-k2-thinking)的原因:即使前序模型全部配额耗尽,链路也不会中断,而是平滑落到免费兜底模型上。更完整的 Combo 设计(预算限制、模型启用/禁用、配额重置时间编排)可参考 combos.md。
2. Rate Limiting:429 与 "Too many requests"
2.1 症状与成因
症状:返回Rate limit exceeded或Too many requests(HTTP 429)。
成因:
- 订阅配额耗尽(5 小时 / 每日 / 每周窗口);
- 触达 API 速率限制;
- 并发请求过多。
2.2 标准处置流程
- 查看重置倒计时:
Dashboard → Quota Tracking → 查看重置倒计时,明确"何时能恢复"; - 切到便宜档:改用
glm/glm-4.7($0.6/1M tokens)或minimax/MiniMax-M2.1($0.20/1M tokens); - 增加回退 Combo:主模型用订阅(如
cc/claude-opus),备份用便宜模型,紧急用免费模型(if/kimi-k2)。
2.3 源码纵深:指数退避与错误规则引擎
9Router 对限流有一套配置驱动的错误分类引擎,见 open-sse/config/errorConfig.js:
- 文本规则优先匹配(
rate limit、too many requests、quota exceeded、capacity、overloaded等关键词)→ 触发指数退避; - 状态码规则兜底(401/402/403/404 固定冷却 2 分钟,429 走退避);
- 退避基准
base: 2000ms、上限max: 5 * 60 * 1000(5 分钟)、最大等级 15(errorConfig.js),即 2s → 4s → 8s 逐级翻倍; - 未匹配的瞬时错误统一走 30 秒冷却(
TRANSIENT_COOLDOWN_MS)。
配额追踪的完整能力(实时 token 消耗、重置倒计时、成本估算、预算告警、免费档自动切换)以及GET http://localhost:20128/api/quota的查询方式,见 quota-tracking.md。此外,账户级冷却状态(rateLimitedUntil、backoffLevel)会持久化在连接记录上,applyErrorState/resetAccountState负责写入与成功后的复位(open-sse/services/accountFallback.js),请求成功时自动清零冷却状态。
3. OAuth Token 过期:Unauthorized / Token expired
3.1 症状与成因
症状:返回Unauthorized或Token expired。
成因:
- OAuth token 过期(自动刷新失败);
- 提供商会话被服务端失效;
- 刷新期间网络异常。
3.2 标准处置流程
- 等待自动刷新(默认行为):9Router 默认自动刷新 token,等 30 秒后重试即可;
- 手动重连:
Dashboard → Providers → [提供商名] → Reconnect → 重新走一遍 OAuth 授权流; - 检查提供商服务状态:确认 Claude Code、Codex 等服务本身在线。
3.3 源码纵深:Token 刷新是怎么"自愈"的
自动刷新由 open-sse/services/tokenRefresh.js 统一调度:
REFRESH_HANDLERS为每个提供商注册专用刷新函数(Claude、Codex、Gemini CLI、Antigravity、Kimi、iFlow、Kiro、GitHub、Copilot、Trae、Zed、Windsurf 等,见 tokenRefresh.js);refreshWithRetry默认最多重试 3 次,每次间隔递增(1s、2s),见 tokenRefresh.js;- 令牌到期前有 5 分钟缓冲(
TOKEN_EXPIRY_BUFFER_MS),提前刷新以规避竞态,见 tokenRefresh.js; isUnrecoverableRefreshError会识别invalid_grant、refresh_token_reused等不可恢复错误(tokenRefresh.js)——这类情况等再久也没用,必须走"手动重连"重新授权。
所以官方建议"等 30 秒重试"是有依据的:重试窗口覆盖了刷新请求的网络往返与重试机制;但如果持续报Unauthorized,应直接手动重连,而不是反复空等。
4. Costos altos:成本失控的止血方案
4.1 症状与成因
症状:使用量或账单金额异常升高。
成因:
- 无谓地使用昂贵模型;
- 没有配置便宜档回退;
- 上下文窗口过大导致 token 消耗飙升。
4.2 标准处置流程
- 查看用量统计:
Dashboard → Usage Stats → 查看 token 消耗,定位高成本模型; - 换便宜模型:把
cc/claude-opus(订阅约 $20–100/月)替换为glm/glm-4.7($0.6/1M tokens)或minimax/MiniMax-M2.1($0.20/1M tokens); - 启用免费档:
if/kimi-k2-thinking(免费)qw/qwen3-coder-plus(免费)kr/claude-sonnet-4.5(免费)gc/gemini-3-flash-preview(免费,180K/月)
- 优化提示词:压缩上下文、长回复改用流式输出、对高频提示词做缓存。
4.3 源码纵深:成本为什么能被"看住"
成本控制依赖两套机制协同:
- 配额与成本追踪:
quota-tracking.md展示了按提供商(订阅/便宜/免费)分类的成本明细、成本预估(Dashboard → Costs → Projections)以及预算告警(80%/90%/100% 触发、超额自动切免费档)。底层 API 为GET /api/usage?period=today,返回按模型拆分的 token 与成本; - Combo 预算限制:在 Combo 编辑页可设每日/每月预算上限,达到上限后 9Router 会跳过付费模型、只用免费档(见 combos.md 的 Advanced Configuration 一节)。
实践中"订阅 → 便宜 → 免费"三段式 Combo 是把成本压到最低的标准姿势:订阅档吃掉 80% 流量(零边际成本),便宜档负责余量,免费档兜底。
5. Connection Refused:连不上 localhost:20128
5.1 症状与成因
症状:ECONNREFUSED或Cannot connect to localhost:20128。
成因:
- 9Router 没有运行;
- 端口 20128 被占用或未监听;
- 防火墙拦截了连接。
5.2 标准处置流程
- 启动 9Router:
9routerDashboard 应随之打开在
http://localhost:3000; - 检查端口 20128 是否在监听:
# macOS / Linux lsof -i :20128 # Windows netstat -ano | findstr :20128 - 检查防火墙:
- macOS:System Settings → Network → Firewall;
- Windows:Windows Defender 防火墙 → 允许应用;
- Linux:
sudo ufw allow 20128;
- 改用云端端点:本地网关不可用时(例如 Cursor IDE 部署在其他机器/容器),可将 Endpoint 配置为
https://9router.com/v1。
5.3 源码纵深:CLI 客户端默认就连这个端口
CLI 工具(9router命令本身)通过 cli/src/cli/api/client.js 的makeRequest与网关通信,默认即指向localhost:20128,并通过x-9r-cli-token头完成本机鉴权。因此ECONNREFUSED几乎总是"服务没起来"或"端口被占用/防火墙拦截"两类原因——先lsof -i :20128确认监听,比反复重启客户端更高效。若服务正常但工具仍连不上,再检查防火墙放行规则。
6. El dashboard no abre:Dashboard 打不开
6.1 症状与成因
症状:http://localhost:3000无法加载。
成因:
- 端口 3000 已被其他程序占用;
- 9Router 进程崩溃;
- 浏览器缓存问题。
6.2 标准处置流程
- 确认 9Router 是否在运行:
# 查看进程 ps aux | grep 9router # 查看端口 3000 lsof -i :3000 - 杀掉占用端口的进程:
# macOS / Linux lsof -ti:3000 | xargs kill -9 # Windows netstat -ano | findstr :3000 taskkill /PID <PID> /F - 重启 9Router:
# 停止 pkill -f 9router # 启动 9router - 清理浏览器缓存:Chrome 用
Ctrl+Shift+Delete → 清理缓存,或直接开无痕窗口验证; - 复查防火墙:确保 3000 端口未被拦截。
注意:Dashboard(3000)与网关(20128)是两个独立端口。出现"工具能连、Dashboard 打不开"时,问题几乎必然出在 3000 端口的占用或前端缓存上,与网关无关;反之亦然。
7. Modelo no encontrado:模型 ID 与提供商前缀
7.1 症状与成因
症状:返回Model not found或Invalid model(HTTP 404,错误码model_not_found)。
成因:
- 提供商未连接;
- 模型 ID 拼写错误(最常见是漏掉提供商前缀);
- 提供商处于非活跃状态。
7.2 标准处置流程
- 验证提供商连接:
Dashboard → Providers → 查看状态(绿色 = 活跃); - 检查模型 ID 格式:
正确:cc/claude-opus-4-5-20251101 错误:claude-opus-4-5-20251101 标准格式:[provider-prefix]/[model-name] - 列出当前可用模型:
curl http://localhost:20128/v1/models \ -H "Authorization: Bearer your-api-key" - 重新连接提供商:
Dashboard → Providers → [提供商] → Reconnect。
7.3 源码纵深:前缀为什么不能省
模型 ID 的provider-prefix是 9Router 路由的关键:请求进入后,网关靠斜杠左侧的短前缀(如cc、glm、if、cx、gc、kr)把请求分发到对应提供商的执行器与凭据。这一设计在源码中处处可见:
- Combo 判定逻辑
getComboModelsFromData直接以modelStr.includes("/")区分"单模型"与"Combo 名"(open-sse/services/combo.js); - 提供商注册表为每个提供商定义短前缀,如 CodeBuddy CN 使用
cbcn/glm-5.2(open-sse/providers/registry/codebuddy-cn.js); - 价格归一化时也会剥掉厂商前缀(
deepseek/deepseek-chat → deepseek-chat)以匹配基准价(open-sse/providers/pricing.js)。
因此漏写前缀不仅会 404,还会让价格与能力(视觉/PDF/搜索)判定全部失准。拿到 404 时,第一件事就是用curl /v1/models拉取真实 ID 列表对照,而不是凭记忆拼模型名。
8. Respuesta lenta:慢响应与超时
8.1 症状与成因
症状:请求耗时过长或超时(504 Gateway Timeout)。
成因:
- 提供商端延迟高;
- 网络问题;
- 上下文/回复体量过大;
- 提供商侧限流。
8.2 标准处置流程
- 查看提供商延迟:
Dashboard → Providers → 查看延迟统计; - 换更快的模型:
快速档:cc/claude-haiku-4-5(Haiku 比 Opus 快) gc/gemini-3-flash-preview qw/qwen3-coder-flash - 启用流式输出(首 token 更快到达,体验上显著"变快"):
{ "model": "cc/claude-opus-4-5", "messages": [], "stream": true } - 检查网络延迟:
ping api.anthropic.com ping api.openai.com - 压缩上下文:裁剪历史消息、缩小提示词、在 CLI 工具中启用 context pruning。
8.3 源码纵深:慢请求会怎样被对待
- 流式支持:
stream: true走 SSE 流式通道,9Router 的 streaming handler 逐块转发(相关实现见 open-sse/handlers/chatCore/streamingHandler.js 与 open-sse/utils/sse.js),客户端可在首个 token 到达后立刻开始渲染; - 超时兜底:CLI 客户端
makeRequest内置 30 秒超时(cli/src/cli/api/client.js),超过即返回Request timeout; - 错误码语义:网关将上游 502/503/504 分别映射为
bad_gateway、service_unavailable、gateway_timeout(open-sse/config/errorConfig.js),方便客户端区分"上游故障"与"请求本身问题"。
9. API Key inválida:鉴权失败排查
9.1 症状与成因
症状:返回Invalid API key或Authentication failed(HTTP 401,错误码invalid_api_key)。
成因:
- 复制了错误的 key;
- key 已过期或被删除;
- key 根本没有生成。
9.2 标准处置流程
- 重新生成 key:
Dashboard → Settings → API Keys → Generate New Key → 复制使用; - 核对 key 格式:
正确:9r_xxxxxxxxxxxxxxxxxxxxxxxx 错误:缺少 9r_ 前缀 - 检查 CLI 工具中的配置:
# Cursor Settings → Models → OpenAI API Key # Cline Settings → API Key # 环境变量方式 export OPENAI_API_KEY="9r_your_key" - 用 curl 直接验证 key:
curl http://localhost:20128/v1/models \ -H "Authorization: Bearer 9r_your_key"
9.3 源码纵深:key 的校验发生在哪一层
- 本地部署默认宽松,但面向公网暴露时可通过环境变量
REQUIRE_API_KEY=true强制所有/v1/*路由校验 Bearer key(见 README.md)——这正是"云端点必须配 key"的场景; - key 的管理走
/api/keys接口(创建/删除),由 Dashboard 的 Settings → API Keys 面板调用(cli/src/cli/api/client.js); - 401 错误在错误分类中属于"固定 2 分钟冷却"档(errorConfig.js),即:换了新 key 后仍可能短暂报 401,稍等冷却或重启工具即可,无需反复更换。
10. 仍然解决不了?—— 自检清单与下一步
若上述方案都无效,按此顺序做一次系统性自检:
- 进程与端口:
ps aux | grep 9router、lsof -i :20128、lsof -i :3000三项全部确认; - 配额与冷却:
Dashboard → Quota Tracking查看是否有账户处于冷却(cooldown)状态——存在rateLimitedUntil时请求会直接短路(见 open-sse/services/accountFallback.js); - Token 状态:确认自动刷新是否持续失败,必要时手动 Reconnect;
- 模型 ID:用
curl /v1/models拉取真实 ID 逐一比对; - 升级排障:查阅仓库内相关主题文档 faq.md、combos.md、quota-tracking.md,或到 GitHub Issues 提交包含上述诊断信息的报告。
最后一条经验法则:9Router 的故障绝大多数不是"坏了",而是"配额没了"或"路由链断了"。把"订阅 → 便宜 → 免费"的 Combo 回退链配好、养成每天看一次配额面板的习惯,你遇到的 90% 的报错都会在下次请求时自动消失——这正是它被设计出来的目的。
【免费下载链接】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),仅供参考