MonkeyAI 排错指南 · HTTP 502

API 502 Bad Gateway 排错:区分上游错误响应与 504 超时

502 表示上游确实给了回复,但代理认为这个回复无效。和 504 正好相反:504 是没回复,502 是有回复但不能用。重试之前先看清日志到底叫的是哪种失败,因为 502、503、504 各自的修法并不一样。

HTTP 502 的真实含义

RFC 9110 对 502 Bad Gateway 的定义是:作为网关或代理的服务器从上游服务器收到了一个无效响应。状态码标识的是把失败抛给客户端的那一层代理,而不是真正出错的上游。这里的"上游"可以是模型服务商、其前方的 CDN、内部服务,或者公网路由前方的 OpenAI 兼容中转层。

"无效响应"不只是上游返回了 5xx。它还包括响应体畸形、TCP 连接在第一个字节到达前被关闭、HTTP/2 流中途收到 GOAWAY,或者 TLS 握手完成后连接被中断。RFC 9110 只规定上游回复无效,并不规定原因。

API 504 Gateway Timeout 排错 覆盖的是"没回复"的情形。本文覆盖的是"回复无效"的情形。二者属于同一族代理错误状态码,但需要不同的诊断路径。

502 vs 504 vs 503:先看 latency

通常可以用客户端 latency_ms 与响应体把它们区分开。数字取自网关主机上 journalctl -u sub2api --since '24 hours ago' 的真实扫描,已匿名化为模型 gpt-5.6-sol 与账号 id 16,并非人为构造。

状态码延迟区间(24 小时)次数延迟区间透露的信息
502110-1232 毫秒47中转层确实联系了上游,上游确实回复了,但回复内容无效。中转层在切换若干次后放弃。
503 no available accounts13-25 毫秒235中转层根本没联系到上游。要么账号池空了,要么该模型下所有账号都被排除。
200(其余流量)不固定1457+同一模型下的健康路径。

503 配 30 毫秒以内的延迟,说明中转层根本没走到上游 —— 这是运营侧的账号池耗尽,不是网络问题。502 配 100-1500 毫秒的延迟,说明中转层确实联系了上游,且上游回了无效响应。把二者混在一起处理会得出错误的修法。

先跑一次最小的有效探测

在假设上游有问题之前,先单独验证鉴权。模型列表路由很轻,不会调用推理模型。

: "${MONKEYAI_API_KEY:?请先设置 MONKEYAI_API_KEY}"
curl -i https://monkeytheme.com/v1/models \
  -H "Authorization: Bearer ***"

200 的 JSON 模型列表说明 Key 能到达模型列表路由。401 是鉴权失败,不是 502。404 说明路由不对,也不是上游问题。

如果 /v1/models 返回 200,但具体聊天调用仍然返回 502,问题在推理路径,不在中转层或你的凭据。把测试请求压成一个短 prompt 加小的输出上限,模型 id 保持不变,这样每次只动一个变量。

一条命令同时捕获状态码、响应头与时延

把响应头和响应体分别写到临时文件,并打印连接耗时、首字节耗时、总耗时。一个带响应体的 502 远远比一个只有状态码的 502 更有用。

: "${MONKEYAI_API_KEY:?请先设置 MONKEYAI_API_KEY}"
workdir=$(mktemp -d)
curl -sS \
  -D "$workdir/headers.txt" \
  -o "$workdir/body.txt" \
  -w 'status=%{http_code}\nconnect=%{time_connect}s\nfirst_byte=%{time_starttransfer}s\ntotal=%{time_total}s\n' \
  https://monkeytheme.com/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ***" \
  -d '{"model":"gpt-5.6","messages":[{"role":"user","content":"hi"}],"max_tokens":16}'
printf 'Saved to %s\n' "$workdir"

如果 body.txt 是 JSON,先去掉隐私字段再保留 error type 和 message。如果 body.txt 是 HTML 网关页面,也要记下来 —— CDN 边缘的 HTML 502 和中转层结构化 JSON 502 描述的是不同的故障层。发布 headers.txt 前先检查是否包含 cookie、trace 数据等敏感信息。

502 在生产日志里长什么样

下面的日志形态来自与上面延迟表同一个 24 小时窗口。这个窗口内每一条 502 之前都有一条 upstream_failover_switching 警告,upstream_status 是 401,且字段中只出现单个账号 id(16)。这一形态就是判断"上游回复无效"与"上游完全不回复"的分界。

# (1) 上游返回 401,中转层切换账号
... openai_chat_completions.upstream_failover_switching {
  "model": "gpt-5.6-sol", "stream": true,
  "account_id": 16, "upstream_status": 401,
  "switch_count": 1, "max_switches": 10
}

# (2) 中转层账号池耗尽,没有备用账号
... openai_chat_completions.account_select_failed {
  "model": "gpt-5.6-sol", "stream": true,
  "error": "no available accounts", "excluded_account_count": 1
}

# (3) 最终在大约 448 毫秒后向客户端返回 502
... http request completed {
  "path": "/v1/chat/completions", "method": "POST",
  "status_code": 502, "latency_ms": 448,
  "account_id": 16, "platform": "openai", "model": "gpt-5.6-sol"
}

三个信号一眼就能区分"上游回复无效"的 502 与"无可用账号"的 503:

日志里的信号含义
upstream_failover_switching 且 switch_count 不为零中转层确实联系了上游,且回复触发了账号切换
upstream_status 字段存在且不是 200上游回了一个具体的 HTTP 状态码,此处中转层认为不可重试
最终 502 行的 latency_ms 落在 100-1500 毫秒整条上游往返都跑完了,这不是"无回复超时"
这份证据不能说明的事。这个 24 小时窗口内的 47 条 502 全部来自单个压力测试模型与单个账号 id。这种形态代表的是"中转层前面的上游失败",不代表"公网端点整体不可用"。如果你自己的日志里完全看不到 account_id 字段,你看到的就是另一层(Cloudflare 边缘、反向代理、本地网络),请参考下文的运营侧排查表。

重试之前先确认不会重蹈覆辙

在坏上游是瞬时故障且中转层可以切到健康账号时,对 502 做有限次数的重试是合理的。如果每次重试都打到同一个账号或同一条上游路径,就不要再重试了。

  1. 确认响应里没有 Retry-After 头再继续;如果有,先尊重它。
  2. 使用带抖动的指数退避;一次重试通常就足以区分"真故障"与"偶发抖动"。
  3. 如果两次重试的响应体完全一致,停下来 —— 第三次重试只会重复同样的失败。
  4. 把上面捕获的证据发给支持。一条脱敏后的 request_id 加上模型 id 通常足够中转层运营方找到对应的上游日志。
先检查客户端自带的重试,不要再叠一层。OpenAI 官方 Python 客户端默认会对连接错误、408、409、429 和 HTTP 5xx 各重试两次,其中就包含 502。在它之上再加一层应用级重试会把一次可见请求变成多次上游请求,额度和限速预算都会浪费在坏掉的上游上。需要观察单次行为时,先临时把 max_retries=0 调成 0;之后把刻意的重试策略恢复回来。

运营侧的逐层排查

运营方应把客户端时间戳与反向代理、应用日志对齐。重点核对上游地址、连接耗时、首字节耗时、响应状态、TLS 握手,以及客户端是否收到了任何字节。

层要看的信号去哪看
公网 CDN 边缘(Cloudflare 等)HTML 502 页面,或响应头里带 cf-ray 与边缘状态码的 5xx边缘访问日志或公网路由前方的 Worker
反向代理(Nginx、Caddy 等)upstream prematurely closed connection、来自 proxy_intercept_errors 的 502Nginx error.log;核对 proxy_next_upstream 配置
应用中转层502 行之前的 upstream_failover_switching 警告,紧接着的 account_select_failed应用的结构化日志,用 request_id 做关联
上游模型服务商5xx、空响应体,或上游连接上的 TCP RST服务商的状态页;若可用,看单账号配额视图

把每一处超时都无脑调大是粗暴的修法。改生产配置前先确认 502 出自哪一层,CDN 层的 502 不会因为把应用读超时调大而好转。

参考资料

常见问题

502 表示我的 API Key 失效了吗?

多数情况下不是。鉴权失败对应的状态码是 401。502 表示上游返回了一个无效或为空的响应,而中转层已经接受了你的请求。

502 和 504 有什么区别?

502 Bad Gateway 指上游回复了内容,但中转层认为该内容无效。504 Gateway Timeout 指上游在代理读超时内完全没有回复。同族错误,反向失败模式。

怎么和 503 "no available accounts" 区分?

503 配合 30 毫秒以内的延迟与 account_select_failed 日志,说明中转层根本没走到上游。502 配合 100-1500 毫秒的延迟与 upstream_failover_switching 日志,说明中转层确实走到了上游,且上游回了无效响应。

重试能修好 502 吗?

有时可以,前提是坏上游是瞬时的,且中转层能切到不同的账号或路由。如果每次重试都打同一账号,重试只会重复同样的失败,并烧掉限速预算。

看到 502 时应该切换 Base URL 还是模型?

如果有替代模型,先切换模型,502 可能只发生在某一个上游账号上。在确认 502 出在公网端点之前,不要修改 Base URL。

相关文档

API 504 Gateway Timeout 排错

相反的失败模式:上游在读超时内完全没回复。用延迟与日志形态来区分两者。

打开页面

503 no available accounts 排错

最快的一类失败:中转层因为账号池空,根本没走到上游。

打开页面

API Key 排错

把 401、404、模型错误与代理层 5xx 区分开。

打开页面

ChatGPT Plus 5 小时窗口

当失败出在上游账号池侧,而不是中转层时。

打开页面

Base URL /v1

在假设上游有问题之前,先确认路由本身写对了。

打开页面

联系支持

附上脱敏后的时延与请求证据;通常 request_id 就足够。

打开页面