OpenRouter 报 No allowed providers are available 怎么恢复

2026-08-31

数据截至 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 对象,记录路由器这一趟到底做了什么。这个头的取值按官方说明是大小写不敏感的,只认 enableddisabled,任何其它值(包括拼错的、空字符串、没见过的等级)都会退回 disabled;不带这个头时的默认行为也是 disabled。另外还有一个旧头名 X-OpenRouter-Experimental-Metadata 仍然被接受,官方建议逐步迁移到新名字。

关键在于:错误响应也带这个元数据。官方说明写得很清楚,开启后错误响应会在错误信封的顶层挂上 openrouter_metadata,与 error 平级而不是嵌在里面,四条补全路由(chat completions、messages、responses、旧版 completions)以及流式与非流式都适用。

拿到之后重点看这几个字段:

  • requested:客户端送进来的模型 slug 或别名。如果你用了别名或变体后缀,这里能确认路由器最终解析成了什么。
  • strategy:这次用的路由策略,官方列出的取值有 directautofreelatestaliasfallbackparetobodybuilderfusion(以官方文档为准)。
  • endpoints.totalendpoints.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:按量化级别过滤。官方列出的取值包括 int4int8fp4mxfp4nvfp4fp6fp8mxfp8fp16bf16fp32unknown(以官方文档为准)。挑一个冷门量化级别,很容易一个供应商都不剩。
  • max_price:一个对象,声明你能接受的最高供应商定价,可用 promptcompletion 两个维度,部分供应商支持按请求计价时可用 request,按图计价时可用 image具体价格数字请以官方定价页为准,本文不列。前面引用过的官方说明里,attempt: 0 的成因就直接点名了最高价过滤器把候选全否掉这一种。

多个条件是叠加生效的。单看每一条都合理,凑一起就可能一个端点都活不下来——这也是为什么排查要从”全部去掉”开始,而不是从”改一改”开始。

账户级设置是天花板,请求参数只能更严

这是最容易漏查的一层:报错来自你几个月前在网页端勾过的一个开关,而不是这次的请求体。

官方在讲请求级允许清单时给了一条提示:你可以在隐私设置里为账户全部请求配置允许的供应商,这份配置对所有 API 请求和聊天室消息生效;当你又在单次请求里指定 only 时,两重限制同时成立——账户级允许清单是天花板,请求的 only 在这个天花板之内再收窄。两者没有交集,就是 404。忽略清单的叠加方式不一样,官方说明是请求级的忽略列表会与账户级的忽略列表合并

组织与工作区场景还有一层。官方对权限的说明是,账户级由组织管理员管理数据策略与允许的供应商/模型,工作区继承账户级的数据策略与允许清单,在这个约束内可以设置更细的 guardrail 进一步限制——账户级策略是上限,工作区只能更严。guardrail 本身也带 allowed_providersallowed_models 配置项,可以通过管理 API 修改。所以在团队环境里遇到这条报错,请求体干净不代表没问题,得往上翻一层看工作区默认 guardrail。

base slug 匹配的那个坑

还有一个纯粹是写法导致的空池子。官方说明:在 orderonlyignore 任一字段里使用基础供应商 slug(例如 google-vertex)时,它会匹配该供应商的全部端点,包括各种变体与区域(google-vertex/us-east5google-vertex/us-central1 等)。但服务层级端点是例外——像 openai/fastgoogle-vertex/flex 这类带层级后缀的端点不会被基础 slug 匹配到,必须通过 service_tier 参数或带层级后缀的 slug 显式选入。反过来,想精确锁定某个区域或变体,就得写全 slug。

BYOK 场景下还有一条相关机制:当某个供应商的密钥策略设成从不使用共享容量时,如果没有密钥允许请求的模型、或者所有匹配的密钥都失败了,官方说明是该供应商会被直接跳过而不是回落到平台端点;其它供应商仍可服务这个请求——除非你又用 provider.only 之类的方式把它们限制掉了。这两个设置撞一起,就是典型的”哪儿都没走通”。

恢复顺序

按这个次序做,基本不会绕路:

  1. 先开元数据头重发一次,确认 attempt 是不是 0。是 0 就锁定筛选问题,不是 0 说明供应商试过且全失败了,那是另一条排查线,参考路由失败的分层定位
  2. 把请求体里 provider 对象整个去掉再发一次。能通,说明问题在请求级筛选;仍不通,去账户隐私设置和工作区 guardrail 里找。
  3. 对照报错消息的两个名单。前半句给的就是当前在服务这个模型的供应商,把你的 only 改成其中之一,或者直接删掉 only 让路由器自己挑。
  4. 确认模型 slug 本身。用了 :extended 就换回基础 slug;不确定某个模型有哪些端点,可以用官方的 GET /models/{author}/{slug}/endpoints 列出该模型的全部端点,用 GET /api/v1/models 列出全量模型。
  5. 想保留约束又要容错,考虑用 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,想控制成本应该用 sortmax_price,把 only 留给那些确实不能路由到别家的合规场景。收得越紧,恢复能力越差,这是官方在文档里反复提醒的同一件事。

另一个坑是把这条 404 当成偶发故障去重试。它不是偶发的:只要你的筛选条件不变、模型的供应商构成不变,重试多少次都是同一个结果。真正该做的是改约束,或者改模型。接入阶段就把这些字段的语义搞清楚,比线上出问题再翻文档划算得多,参考OpenRouter 接入配置

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。

留言讨论

评论发布后会被人工复核,违规内容将被删除。

    还没有人评论,来说说你的看法

    如果发表没有反应,可以前往联系我们告诉我们。

    OpenRouter 充值不方便?

    国内直连的 OpenAI 兼容端点,一期提供 DeepSeek,注册送 ¥5。

    看替代方案

    这个页面有问题?

    提交时会附带当前页面地址和浏览器信息,帮助我们定位问题。不填联系方式即为匿名。