ChatGPT Plus 5 小时窗口:OAuth 账号池为什么返回 503
如果你的 OpenAI 兼容中转站返回 HTTP 503,响应体是 {"error":{"message":"Service temporarily unavailable","type":"api_error"}},而中转日志写着 openai_chat_completions.account_select_failed error="no available accounts",问题通常不在你的 key。它来自 ChatGPT Plus OAuth 账号池的滚动 5 小时窗口被单一账号打到零,failover 已经没有可用的替代账号。
滚动 5 小时窗口到底是什么
ChatGPT Plus 账号会被施加一种滚动使用配额,OpenAI 大约按 5 小时的节拍重置。OpenAI 官方错误文档并没有把精确预算写进 API 客户端可见的错误面 — Error codes guide 里没有 504,也没有 "rolling window exhausted" 这一行。从中转侧观察到的是一个滑动配额,当单一 Plus 账号流量持续偏高时,这个配额会被打到零。
窗口按账号计算,不按模型。在同一个 Plus OAuth 登录下切换模型不会重置计时器,也不会给你新的预算。
为什么客户端拿到的是 503 而不是 429
中转并不会把 OpenAI 的内部配额信号作为 429 直接吐给客户端。我们 2026-08-07 在生产日志中观察到的链路是这样的:
# 中转日志(节选自 journalctl -u sub2api)
WARN openai_chat_completions.upstream_failover_switching
upstream_status=401 account_id=16 switch_count=1 max_switches=10
model=gpt-5.6-sol stream=true
WARN openai_chat_completions.account_select_failed
error="no available accounts" excluded_account_count=0
INFO http request completed status_code=503 latency_ms=16 model=gpt-5.6-sol
逐步拆解:
- 中转选出一个 ChatGPT Plus 账号(我们的日志里是 account_id 16),把请求转发过去。
- OpenAI Plus 给中转返回 401,意味着这个账号的滚动窗口已经耗尽,它的 token 在推理流量上被视作失效。
- 中转把这个账号标记为耗尽,开始 failover — 我们观察到的配置最多切换 10 次。
- 池子里其它 Plus 账号状态相同,每次切换都拿到 401。
- 切换预算耗尽后,中转向客户端返回 503,JSON 体里写 "no available accounts"。
中转吸收了 OpenAI Plus 的配额信号,然后翻译成自己的 503。客户端永远看不到那个上游 401。
这和客户端直接收到 401 不是一回事
| 看到什么 | 失败层 | 去哪里查 |
|---|---|---|
| 中转返回 401,响应体提到 token 无效 | 你的 key 或 header | Authorization header、key 前缀、中转上的账号 |
| 中转返回 404 | 路由不匹配 | Base URL、endpoint 路径、模型 id |
| 429 带结构化响应体并附 Retry-After | 单账号的配额,上游是健康的 | 降并发,尊重 Retry-After |
| 多次上游 401 之后 503 报 "no available accounts" | 中转的 Plus OAuth 账号池 — 滚动 5 小时窗口 | 服务端日志:upstream_failover_switching 后接 account_select_failed |
| 中转返回 504 | 反代计时器 | proxy_read_timeout、路由、上游延迟 |
动手之前先做一个小验证:用同一个 key 调 /v1/models,应该返回 200 JSON 模型列表。如果这条通了,而 /v1/chat/completions 返回 503,失败就在推理账号池,不在你的请求结构里。
正确读中转日志
三行日志连在一起,就可以指认 ChatGPT Plus 5 小时窗口故障。按顺序找:
| 日志行 | 它告诉你什么 |
|---|---|
openai_token_cache_get_failed account_id=16 error="redis: nil" | 中转读这个账号缓存 token 失败。通常是该账号被回收的第一个信号。 |
openai_chat_completions.upstream_failover_switching upstream_status=401 account_id=16 | 中转尝试了这个账号,拿到 OpenAI 的 401,正在切到池里的下一个账号。 |
openai_chat_completions.account_select_failed error="no available accounts" excluded_account_count=0 | 池子空了。0 表示在最终选择阶段没有任何账号被真正尝试过 — 中转在第二次选择之前就放弃了。 |
如果 excluded_account_count 大于 0,说明 failover 实际把所有账号都试了一遍,每个都被排除。如果是 0,说明中转连第二次选择都没启动。两个数字对应的是同一个根因:ChatGPT Plus 池子现在接不了你的请求。
客户端该怎么做
- 立刻停止紧贴重试。诊断期间把
max_retries=0。 - 并发量至少砍掉一半。窗口是按速率计算的,不是按请求数。
- 等滚动窗口慢慢恢复。恢复是渐进的 — 09:00 烧光预算的用户到 14:00 只是部分恢复,不是满血。
- 如果你的负载稳定且无法接受这个节奏,换到不同档位:付费 OpenAI API key 不和 Plus 5 小时窗口共用预算。
- 联系支持之前,先记下 request_id、UTC 时间、模型、中转返回的 "no available accounts" 响应体。把 API key 涂掉。
*** 代替。request_id 和中转的 account_select_failed 日志行是定位最强的两个线索。运营侧该查什么
如果你运营这个中转,同样的生产模式给你的是一小组真正能做的事:
- 确认失败的请求都集中在 Plus OAuth 池上的某个
account_id,而不是 API key 池。 - 检查 Antigravity token refresher 是否按计划打印
Account N needs refresh。如果 refresh 健康、上游依然 401,问题在上游配额,不在 token 缓存。 - 每天跟踪
upstream_failover_switching的次数。一个稳定的计数(我们 7 天看到 241,每天比较均匀)是 Plus 池逼近 5 小时上限的早期预警信号。 - 告诉用户降并发,不要为了这个故障去调
proxy_read_timeout。上游已经返回 401 了,定时器无关。
常见问题
什么是 ChatGPT Plus 的 5 小时窗口?
OpenAI 对 Plus 账号施加的一种滚动配额。官方文档没把精确预算写进错误面。实际表现:一个滑动窗口,单一 Plus 账号持续高负载下会被打到零。
为什么客户端收到的是 503 而不是 429?
中转吸收了 OpenAI Plus 的信号,上游 401 触发的 failover 把池子耗尽后,中转向客户端返回 503 "no available accounts"。
换模型能解决吗?
不能。窗口按账号计算,不按模型。同一个 Plus 账号,同一个窗口。
等满 5 小时会恢复吗?
窗口是滚动的。09:00 烧光预算的用户到 14:00 只是部分恢复,不是满血。恢复是渐进的。
看到 503 应该加大重试吗?
不应该。降并发,等滚动窗口恢复,或换到不同档位的账号。
服务端怎么确认?
找 upstream_failover_switching 的 upstream_status=401,后接 account_select_failed 的 error="no available accounts"。
这和直接收到 401 是一回事吗?
不是。客户端直接 401 说明你的 key 失效。在上游多次 401 之后收到 503 "no available accounts",说明你的 key 没问题,问题在中转的 Plus OAuth 池被耗尽。