超时与流式中断
网关等待上游时有 6 分钟的空闲超时。连接上游失败或等待超时,网关会先换下一个通道,全部失败后返回 500 或 504,响应体是 HTML 页面而不是 JSON。流式请求一旦开始输出就不再换通道,中途出错时网关在流末尾补发一个错误事件后结束连接。客户端应把没有收到结束标记的流当作失败处理,并自行决定重试和去重。
504 / 500:响应体是 HTML 页面
| 状态码 | 原因 | 日志「状态说明」 |
|---|---|---|
| 504 | 等待上游响应超时,或连接上游超时 | 「网关」+「上游请求超时」 |
| 500 | 连接上游失败(例如连接被拒绝),或读取上游响应时出错 | 「网关」+「连接上游失败,未收到响应」或「上游请求或响应读取失败」 |
- 这两种响应没有 JSON 错误体,返回的是一个 HTML 错误页,部分 SDK 会报「无法解析响应」一类的错误。请按 HTTP 状态码处理,不要依赖
error.message。 - 返回之前,网关已经按失败重试与备用模型的规则尝试过其他通道,所有候选都失败后才返回最后一次的结果。
- 「状态说明」的来源显示「网关」,因为是网关终止了这次请求,但原因在网关与上游之间的连接。持续出现时,查看服务状态或换用其他模型。
上游超时怎么计算
网关调用上游时的读超时为 360 秒(6 分钟):
- 非流式请求:上游 6 分钟内没有返回响应即超时。生成很长的内容时,请改用
stream: true。 - 流式请求:按相邻两段数据之间的间隔计算,只要上游持续有输出就不会超时;开始输出之前同样最多等待 6 分钟。
- 换通道后重新计时:一个通道超时后,网关改用下一个通道并重新等待,所以一次请求的总耗时可能超过 6 分钟。
流式输出开始后出错
流式请求的响应头和第一段内容一旦发出,网关就不能再换通道。此时上游中断或超时,网关在流的末尾补发一个错误事件,然后结束连接:
Chat Completions:
data: {"error": {"message": "upstream timed out after the response had started", "type": "api_error", "code": "stream_interrupted"}}
Anthropic Messages(/v1/messages):
event: error
data: {"type": "error", "error": {"type": "api_error", "message": "upstream timed out after the response had started"}}
Responses(/v1/responses):补一个 response.failed 事件,response.error.code 为 server_error。
event: response.failed
data: {"type": "response.failed", "sequence_number": 42, "response": {"id": "resp_...", "object": "response", "status": "failed", "error": {"code": "server_error", "message": "upstream timed out after the response had started"}}}
- 超时时
message为upstream timed out after the response had started;连接或读取失败时为upstream failed after the response had started。 - 上游服务商自己在流中发出的错误事件(例如 Anthropic 的
overloaded_error)会按你调用的协议转给你。 - OpenAI 和 Anthropic 的官方 SDK 收到对应格式的错误事件会抛出 API 错误。
- 在日志中,这类请求的「状态」是 200(响应头已经发出),「状态说明」显示「网关」+「上游请求超时」或「上游请求或响应读取失败」。
没有结束标记就当作失败
出现错误事件后,流中不会再有结束标记。请以协议规定的结束标记判断输出是否完整,没有收到就把这次调用视为失败:
| 接口 | 正常结束的标记 |
|---|---|
| Chat Completions | data: [DONE] |
| Anthropic Messages | message_stop 事件 |
| Responses | response.completed 事件 |
上游直接断开连接、没有发出任何错误事件时,流也会在没有结束标记的情况下结束,同样按失败处理。各协议的事件结构见流式响应。
客户端与代理设置
- 客户端超时:设得比 6 分钟长,避免客户端先断开;流式请求优先设置读取间隔超时,而不是整次请求的总超时。
- 按完整事件解析:SSE 事件以空行分隔,一个网络数据块可能只包含半个事件,也可能包含多个事件,不要把每个数据块当作一段完整 JSON。
- 自建代理:如果应用和 LYRouter 之间还有 Nginx 等反向代理,请关闭响应缓冲(Nginx 中设置
proxy_buffering off),并把代理的读超时设得比 6 分钟长,否则流式内容会被攒成一大块才到达,或被代理提前断开。
重试与去重
- 流式中断:收到
stream_interrupted、response.failed,或没有收到结束标记时,不要把已经收到的部分输出当作完整结果。重新调用会重新生成,内容可能不同,也可能再次触发工具调用;已经执行过的工具操作需要由应用去重,例如为每个操作携带业务上的幂等标识。 - 504 / 500:网关已经在内部换过通道,客户端使用有上限的指数退避重试即可,不要无间隔地连续重试。
- 持续失败:同一个 API Key 在 60 秒内大量请求失败会被临时熔断,返回 429
api_key_circuit_open,见429:限流与配额。
用日志定位
在请求日志中找到这次请求,打开详情:
- 各阶段耗时:「④ 上游调用」占「总耗时」的大部分,说明时间花在上游;旁边显示「· N 次尝试,最长 X」时,说明换过通道,每次尝试都可能等待了接近 6 分钟。
- 首 Token:日志列表的「首 Token」列(在 自定义列 中打开)显示上游开始输出前等待了多久。
- 路由追踪:「上游调用尝试」中每次尝试的状态显示「超时」「连接失败」等,见查看实际路由。
图 1:在日志详情的各阶段耗时中比较总耗时、上游耗时和首 Token 时间。 截图为演示数据,点击图片可查看原图。
常见问题
客户端报超时,日志里这次请求却是 200?
答: 通常是客户端或中间的代理先断开了连接,网关仍然完成了这次调用。用日志的「总延迟」和客户端的超时设置比较;日志的「时间」是请求结束的时间,减去「总延迟」就是请求到达网关的时间。把客户端超时调到 6 分钟以上,或改用流式请求。
流式响应很久没有第一段内容?
答: 先看日志的「首 Token」。首 Token 很长说明上游迟迟没有开始输出,可以稍后重试或换用其他模型;首 Token 正常但客户端迟迟收不到内容,检查中间代理是否缓冲了响应。
流式响应解析 JSON 失败,应该检查什么?
答: 确认解析器按完整 SSE 事件读取,并能处理 data: [DONE] 这类不是 JSON 的行。如果请求在输出开始前就失败,返回的是普通的错误响应而不是事件流:网关拒绝的请求通常是 JSON 错误体,500、504 是 HTML 页面,请先检查状态码和 Content-Type。
提交超时问题时需要提供哪些信息?
答: 网关的响应中不包含请求 ID。请在日志中找到这次请求,附上日志详情顶部的 Request ID、调用时间、模型、是否流式、客户端的超时设置,以及客户端收到的报错原文或最后收到的几个事件。
