OpenRouter 报 No allowed providers are available 怎么恢复
数据截至 2026-08,价格与限额以各官网为准。本文只讲计费与接入机制,不列具体价格数字。
这条报错的语义比它看上去窄得多:它不是说模型下线了,也不是说平台没有供应商,而是说”确实有供应商在跑这个模型,但你自己给的允许清单把它们全排除了”。官方文档里的示例消息把这层意思写得很直白——它会同时告诉你「谁在服务这个模型」和「你的偏好只允许了谁」,两个名单一对比,缺口就摆在眼前。所以恢复路径不是去换模型、去充值、去重试,而是回头看你在 provider 对象里写了什么、账户隐私设置里勾了什么。绝大多数情况下,把那个过窄的允许清单放宽,或者干脆去掉,请求立刻就通了。
先把这条消息逐字读完
官方文档里给出的这条错误响应,消息体是这样一句(这里保留官方示例的原样结构):
No allowed providers are available for the selected model. Providers serving openai/gpt-4o-mini: openai, but your request's provider.only preference permits only: azure.
这句话被冒号切成了两半,两半都是给你看的:前半句列出的是当前实际在服务这个模型的供应商,后半句列出的是你这次请求的 provider.only 偏好放行的供应商。示例里 OpenAI 在服务这个模型,而请求只允许 Azure,交集为空,于是路由器无路可走。
对应的 HTTP 状态是 404。这一点值得单独说一句:站在使用者的直觉里,404 通常意味着”东西不存在”,容易让人以为模型被下架了。但在这条报错里,模型是存在的、供应商也是在线的,404 表达的是”在你给定的约束下,没有可路由的端点”。官方在讲请求级允许清单的说明里也重复了同一条规则——如果没有任何供应商同时满足账户级与请求级的限制,请求就会以 404 失败。
顺带把邻近的两个状态区分开,免得排查时走错方向:官方错误码说明里,401 是 key 缺失或无效,402 是额度不足,429 是触发速率限制,529 是上游供应商临时过载。而 403(permission_denied)在官方定义里是 key 有效但缺少所需权限,或者请求被 guardrail 拦截了——它不是区域限制,也不是本文这条。这几类各有各的处理路径,别混着查。
官方明确写过的两种成因
事实上,能在官方文档里直接找到出处的成因只有两条,其余的都属于”同类机制推出来的可能性”,得靠元数据去证实,不能靠猜。
第一条是 provider.only 设得过窄,也就是上面那条示例消息描述的场景。官方在讲这个字段时专门挂了一条警告:只允许部分供应商会明显削减 fallback 选项,限制请求的恢复能力。这句话的潜台词是,only 不只是一个偏好,它会把整条容错链一起砍掉——平时有多个供应商轮换兜底的模型,一旦被限制到单家,那家一有波动,你就没有退路。
第二条是请求了已废弃的 :extended 变体。 官方文档给这个变体挂了 Deprecated 警告,说明当前 OpenRouter 上没有任何模型提供该变体,请求 model:extended 这样的 slug 没有端点可以路由,因此会失败。官方给的替代做法是直接用基础模型 slug,然后挑一个标准上下文长度装得下你输入的模型;每个模型的 context_length 以及它确实支持的静态变体,都能在模型浏览页和模型 API 里查到。至于这种情况下返回的具体错误文案,官方文档里没有找到相关说明,所以别默认它和上面那句一模一样。
用路由元数据头看路由器筛到了哪一步
猜成因不如让路由器自己说。OpenRouter 提供了一个逐请求开启的路由元数据能力:在请求头里加上 X-OpenRouter-Metadata,值填 enabled,响应里就会多出一个 openrouter_metadata 对象,记录路由器这一趟到底做了什么。这个头的取值按官方说明是大小写不敏感的,只认 enabled 和 disabled,任何其它值(包括拼错的、空字符串、没见过的等级)都会退回 disabled;不带这个头时的默认行为也是 disabled。另外还有一个旧头名 X-OpenRouter-Experimental-Metadata 仍然被接受,官方建议逐步迁移到新名字。
关键在于:错误响应也带这个元数据。官方说明写得很清楚,开启后错误响应会在错误信封的顶层挂上 openrouter_metadata,与 error 平级而不是嵌在里面,四条补全路由(chat completions、messages、responses、旧版 completions)以及流式与非流式都适用。
拿到之后重点看这几个字段:
requested:客户端送进来的模型 slug 或别名。如果你用了别名或变体后缀,这里能确认路由器最终解析成了什么。strategy:这次用的路由策略,官方列出的取值有direct、auto、free、latest、alias、fallback、pareto、bodybuilder、fusion(以官方文档为准)。endpoints.total与endpoints.available[]:路由器考虑过的候选端点快照,以及哪一个被选中。attempt:这个字段是本文的关键。官方解释是,值为 0 意味着请求根本没到达任何供应商,典型原因就是所有候选在提交之前被筛掉了——例如provider.only排除掉了最后一个端点,或者允许清单、最高价过滤器把候选全否了;而值大于等于 1 则意味着尝试过的供应商都失败、fallback 也用尽了。
所以看到 attempt: 0,方向就定死了:是筛选问题,不是可用性问题,不要再去重试、不要去换模型。官方还补了一句,失败时没有任何端点会被标记为 selected,因为确实没有端点返回过成功响应,别把这个当成异常信号。
会把候选池筛空的字段,逐个过一遍
provider 对象里可写的字段不少,它们全都是”减法”,只会让候选池变小,不会变大。排查时把请求体里出现的这些字段挨个注释掉再试,比盲改快得多。
only:只允许列出的供应商 slug。最常见的元凶。ignore:跳过列出的供应商。写多了同样会掏空池子,官方对它也挂了同一条”明显削减 fallback”的警告。allow_fallbacks:默认为true;设成false时,官方说明是不再尝试其它可用供应商,只走首选。和order组合使用会把范围收得更死。require_parameters:默认为false。设成true后,不支持你请求里全部参数的供应商连请求都收不到(默认行为下它们仍能收到,只是忽略不认识的参数)。你多传一个冷门参数,池子可能就空了。data_collection:取值allow(默认)或deny。设成deny时只用不收集用户数据的供应商。zdr:设为true时只路由到具备零数据保留策略的端点。注意官方特别说明,这个逐请求参数与账户级、guardrail 级的 ZDR 设置是”或”的关系——任何一处开启就会生效,请求级参数只能确保开启,无法覆盖掉账户级或 guardrail 级的强制。enforce_distillable_text:设为true时只路由到作者允许文本蒸馏的模型。quantizations:按量化级别过滤。官方列出的取值包括int4、int8、fp4、mxfp4、nvfp4、fp6、fp8、mxfp8、fp16、bf16、fp32、unknown(以官方文档为准)。挑一个冷门量化级别,很容易一个供应商都不剩。max_price:一个对象,声明你能接受的最高供应商定价,可用prompt、completion两个维度,部分供应商支持按请求计价时可用request,按图计价时可用image。具体价格数字请以官方定价页为准,本文不列。前面引用过的官方说明里,attempt: 0的成因就直接点名了最高价过滤器把候选全否掉这一种。
多个条件是叠加生效的。单看每一条都合理,凑一起就可能一个端点都活不下来——这也是为什么排查要从”全部去掉”开始,而不是从”改一改”开始。
账户级设置是天花板,请求参数只能更严
这是最容易漏查的一层:报错来自你几个月前在网页端勾过的一个开关,而不是这次的请求体。
官方在讲请求级允许清单时给了一条提示:你可以在隐私设置里为账户全部请求配置允许的供应商,这份配置对所有 API 请求和聊天室消息生效;当你又在单次请求里指定 only 时,两重限制同时成立——账户级允许清单是天花板,请求的 only 在这个天花板之内再收窄。两者没有交集,就是 404。忽略清单的叠加方式不一样,官方说明是请求级的忽略列表会与账户级的忽略列表合并。
组织与工作区场景还有一层。官方对权限的说明是,账户级由组织管理员管理数据策略与允许的供应商/模型,工作区继承账户级的数据策略与允许清单,在这个约束内可以设置更细的 guardrail 进一步限制——账户级策略是上限,工作区只能更严。guardrail 本身也带 allowed_providers 和 allowed_models 配置项,可以通过管理 API 修改。所以在团队环境里遇到这条报错,请求体干净不代表没问题,得往上翻一层看工作区默认 guardrail。
base slug 匹配的那个坑
还有一个纯粹是写法导致的空池子。官方说明:在 order、only、ignore 任一字段里使用基础供应商 slug(例如 google-vertex)时,它会匹配该供应商的全部端点,包括各种变体与区域(google-vertex/us-east5、google-vertex/us-central1 等)。但服务层级端点是例外——像 openai/fast、google-vertex/flex 这类带层级后缀的端点不会被基础 slug 匹配到,必须通过 service_tier 参数或带层级后缀的 slug 显式选入。反过来,想精确锁定某个区域或变体,就得写全 slug。
BYOK 场景下还有一条相关机制:当某个供应商的密钥策略设成从不使用共享容量时,如果没有密钥允许请求的模型、或者所有匹配的密钥都失败了,官方说明是该供应商会被直接跳过而不是回落到平台端点;其它供应商仍可服务这个请求——除非你又用 provider.only 之类的方式把它们限制掉了。这两个设置撞一起,就是典型的”哪儿都没走通”。
恢复顺序
按这个次序做,基本不会绕路:
- 先开元数据头重发一次,确认
attempt是不是 0。是 0 就锁定筛选问题,不是 0 说明供应商试过且全失败了,那是另一条排查线,参考路由失败的分层定位。 - 把请求体里
provider对象整个去掉再发一次。能通,说明问题在请求级筛选;仍不通,去账户隐私设置和工作区 guardrail 里找。 - 对照报错消息的两个名单。前半句给的就是当前在服务这个模型的供应商,把你的
only改成其中之一,或者直接删掉only让路由器自己挑。 - 确认模型 slug 本身。用了
:extended就换回基础 slug;不确定某个模型有哪些端点,可以用官方的GET /models/{author}/{slug}/endpoints列出该模型的全部端点,用GET /api/v1/models列出全量模型。 - 想保留约束又要容错,考虑用
models数组做模型级 fallback:官方说明是按优先级给一组模型 ID,首个模型报错时自动尝试下一个;如果备选模型也失败,OpenRouter 会把那个错误返回给你。
拿不到元数据的几种情况
最后交代边界,免得你对着空的 openrouter_metadata 反复重试。官方列了几条:
- 缓存命中不带元数据。 流式与非流式的缓存重放都会剥掉这个字段,官方说这是有意为之,避免客户端把行为绑死在陈旧的路由数据上。
- 500 响应不带。 状态为 500 的响应会被清洗成通用消息,元数据也一并省略;其它 5xx(502、503、504、529)在客户端开启了开关时仍会带上。
- 鉴权与限流类失败不带。 这类错误以及其它在路由器还没有可用路由状态之前就触发的错误(例如 API 边缘的校验拒绝)不会包含该字段。限流那条另见429 的通用处理思路。
真遇到需要事后追溯的情况,官方给的路径是用响应头里的 X-Generation-Id 去调 GET /api/v1/generation 取生成记录。这个头在所有补全类端点的响应上都有。
最容易栽的坑
把 provider.only 当成”优先用谁”来写——它是硬性允许清单,不是排序偏好。想表达优先级应该用 order,想控制成本应该用 sort 或 max_price,把 only 留给那些确实不能路由到别家的合规场景。收得越紧,恢复能力越差,这是官方在文档里反复提醒的同一件事。
另一个坑是把这条 404 当成偶发故障去重试。它不是偶发的:只要你的筛选条件不变、模型的供应商构成不变,重试多少次都是同一个结果。真正该做的是改约束,或者改模型。接入阶段就把这些字段的语义搞清楚,比线上出问题再翻文档划算得多,参考OpenRouter 接入配置。
留言讨论
评论发布后会被人工复核,违规内容将被删除。
如果发表没有反应,可以前往联系我们告诉我们。