OpenRouter 把我们的 OpenAI 系全封了,排查与止血的完整过程

2026-08-05 · 约 9 分钟

2026-08-05 的真实故障。所有请求与响应都是当天实测记录。

早上用户报了个奇怪的现象:文案功能正常,出图偶尔失败,而失败时的提示是「该选题文案触发了内容审核,请调整文案后再试」。他改了三遍文案,没用。

最后查出来的根因跟文案一点关系都没有。过程里踩到的每个坑都挺典型,记录下来。

第一层:不是内容审核,是两条腿都断了

先看服务端日志:

[GPT-IMAGE-2·OpenAI] 异常 · 钥=OPENAI_IMAGE_API_KEY_ASSET · fetch failed
[GPT-IMAGE-2·OpenAI] 换钥重试 → OPENAI_IMAGE_API_KEY
[GPT-IMAGE-2·OpenAI] 异常 · 钥=OPENAI_IMAGE_API_KEY · HTTP 429: You have no credits remaining
[单帧·OpenRouter] GPT-IMAGE-2 · OpenAI失败后回落
[GPT-IMAGE-2·OpenRouter] 异常 · HTTP 403: The request is prohibited due to a violation of provider Terms Of Service
[单帧] OpenAI/OpenRouter GPT-IMAGE-2 均无图 · 本条失败

三件事叠在一起:主钥遇到瞬时网络失败,换到备用钥发现余额耗尽,回落 OpenRouter 吃 403。

而「内容审核」这个提示是我们自己代码里的 bug。 判定函数里有这么一条:

m.includes("violat")   // 本意是抓 content policy violation

OpenRouter 的 403 原文写的是 prohibited due to a **violat**ion of provider Terms Of Service,正好命中。于是余额耗尽和账号封禁两个基础设施故障,被包装成了「你的文案有问题」。更糟的是它还触发了「命中审核就快速失败」的短路,把剩下那次重试也跳过了。

报错把用户引向错误的方向,比不报错更伤。 这是当天最值得记的一条。

第二层:403 到底是什么级别的封禁

先确认 key 本身有没有问题:

curl -H "Authorization: Bearer $KEY" https://openrouter.ai/api/v1/key
# 200,limit 100,剩余 80,非免费层

key 有效、有钱。那就逐个模型试,全都 403,响应完全一致:

{"error":{"message":"The request is prohibited due to a violation of provider Terms Of Service.",
"code":403,"metadata":{"provider_name":null,
"previous_errors":[{"code":403,"message":"..."}]}}}

注意 metadata.provider_namenull。这个细节很关键——说明拒绝发生在 OpenRouter 选定 provider 之前,不是某一家 provider 返回的响应。

继续缩小范围:

测试 结果
openai/gpt-5.6-sol 403
openai/gpt-5.6-terra 403
openai/gpt-4o-mini(老模型) 403
openai/gpt-image-2(images 端点) 403
强制 provider: {"only":["Azure"]} 仍然 403
moonshotai/kimi-k3 200 正常
qwen/qwen3.8-max 200 正常

结论很清楚:整个 OpenAI 系(含 Azure 托管)对这个账号全禁,与模型、与端点、与请求内容无关——我发的提示词只有 say ok 两个词。

这不是能靠改代码、换 key、充值解决的问题,只能走人工申诉。

第三层:顺带发现视频模型也不能用,但原因不同

排查过程中试了下视频模型,全部 404:

{"error":{"message":"No endpoints available matching your guardrail restrictions
and data policy. Configure: https://openrouter.ai/settings/privacy","code":404}}

这条错误信息比上一个友好得多,直接指出了原因和位置。

去后台一看,账号的 Allowed Providers 是白名单模式(Exclusively enable these providers),当时只放了六家:Azure、OpenAI、Alibaba Cloud Intl、Fireworks、Moonshot AI、Amazon Bedrock。

而这些视频模型各自只有一家 provider:

模型 Provider 在白名单里?
minimax/hailuo-3 Minimax 否 → 404
bytedance/seedance-2.0 Seed 否 → 404
moonshotai/kimi-k3 六家可选(含 Fireworks) 是 → 正常

单一 provider 的模型一旦被白名单排除就彻底不可用;多 provider 的模型能侥幸活下来。 Kimi 能用纯粹因为它有六家可挑。

试过在请求里加 provider: {"data_collection":"allow"} 想绕过,无效——账号级 guardrail 是上限,请求参数只能更严不能更宽。把 Minimax 和 Seed 加进白名单后立刻恢复,实测出片正常。

止血:三处改动

一、把死掉的备胎换成活的。

出图原来的链路是「OpenAI 官方 → OpenRouter」。OpenRouter 那条已经是 403 死的,等于官方一挂就整条断。我们把 EvoLink 接回中间(这条通道一直都在,只是之前从链里摘掉了),链路变成 OpenAI 官方 → EvoLink,OpenRouter 暂时完全摘出。

顺带发现同规格(high 1024×1536)EvoLink 还比官方便宜约两成:$0.148 对 $0.187。

摘除做成了环境变量开关,对方解封后设一个变量就恢复,不用改代码。

二、钥池不再回落到通用钥。

看一眼 secrets 的摘要就发现问题:

OPENAI_API_KEY              e11c392e4c0d6c58
OPENAI_IMAGE_API_KEY        e11c392e4c0d6c58   ← 同一把
OPENAI_IMAGE_API_KEY_ASSET  677021c46ad6c378   ← 出图专钥

出图钥池的设计是「本道专钥 → 共用钥 → 另一道专钥」,而所谓共用钥就是文本调用在用的那把通用钥,余额早就耗尽了。主钥一抖就必然去撞那个 429,白等一轮还给出误导性错误。混用另有副作用:出图会去烧文本账户的额度。

改成配了专钥就只在专钥之间倒,一把都没配才退回共用钥。

三、错误分类先排除账号级故障。

在判内容审核之前先挡掉余额、配额、计费、TOS 封禁、429 这几类。它们都不是用户能通过改文案解决的问题,也不该触发「快速失败」的短路。

几条可复用的经验

同一把 key,不同库的表现可能不一样。 排查时我一度以为 key 失效——Python 的 urllib 全部 403,而 curl 是 200。差别只是 User-Agent:Pexels 那类前面挂了 Cloudflare 的服务,会把默认 UA 直接拒掉。发请求记得带 UA,否则会误判成鉴权问题白折腾半天。

provider_name: null 是个强信号。 网关类服务如果在选定上游前就拒绝,说明是账号级策略而非上游响应,这时候换模型、换参数、重建 key 都没用。

多 provider 的模型天然更抗风险。 同样的白名单策略,Kimi 活了、Seedance 和 H3 全死。选型时把「有几家 provider 可挑」当成一个可用性指标。

备胎要定期验活。 我们的 OpenRouter 回落腿已经死了不知道多久,没人发现,因为主链一直没挂。直到主链偶发抖动那一次,才暴露出根本没有备胎。没被触发过的容灾路径等于不存在。


本文所有请求与响应为 2026-08-05 实测记录。申诉尚在进行中,若后续解封会补充结果。