问题描述
客户端(如 codex / agent 客户端)在流式响应过程中主动断开连接时,ccx 会把这个 broken pipe 写错误记录为渠道/API Key 失败,计入 key 级熔断统计,导致正常的 key 被反复拉黑(5m→10m 恢复),进而拖垮同渠道的其他会话。
复现场景
- 客户端发一个流式请求(
/v1/responses,stream:true),经 ccx 路由到某渠道
- 上游正常出流,但客户端因为自己的超时/取消逻辑(如客户端侧 90s/300s 的 stale 超时,实际任务本身需 220-270s)先断开连接
- ccx 向客户端写数据时收到
write tcp ...: broken pipe
- ccx 把这个 broken pipe 记为「响应处理失败」,触发
标记API密钥失败: <key> (失败次数: N, 恢复时间: 5m0s/10m0s)
- 客户端断开后自动重试 → 循环触发 → key 被 5m/10m 黑名单
日志证据:
[Responses-Key] [session=xxx round=1] 警告: 响应处理失败: write tcp 172.23.0.6:3000->172.23.0.5:36022: write: broken pipe
[Responses-Key] 标记API密钥失败: ark-*** (失败次数: 1, 恢复时间: 5m0s)
关键点:上游本身没有失败(实测该模型首 token 6-11s 正常返回),纯粹是客户端主动断开,但 ccx 把它当成了上游/渠道错误。
影响
- 客户端超时→断开→重试的循环会让一个健康渠道的 key 反复熔断,5m/10m 不可用
- 渠道 key 被熔断期间,其他走该渠道的正常会话也一起失败
- 单渠道模型(无备用)直接表现为「所有渠道都失败了」503
期望行为
客户端主动断开(broken pipe / 客户端侧 connection reset)不应计入 key/渠道的失败熔断统计,或至少应可配置(如 countClientCancelAsFailure: false)。这是客户端取消,不是上游故障。
相关佐证
- 请求
Accept: application/json(非流式解析)与 "stream":true body 并存时也容易触发
- 客户端
X-Stainless-Read-Timeout: 1800 表明 SDK 层 read 超时很长,断开是客户端应用层主动取消
问题描述
客户端(如 codex / agent 客户端)在流式响应过程中主动断开连接时,ccx 会把这个
broken pipe写错误记录为渠道/API Key 失败,计入 key 级熔断统计,导致正常的 key 被反复拉黑(5m→10m 恢复),进而拖垮同渠道的其他会话。复现场景
/v1/responses,stream:true),经 ccx 路由到某渠道write tcp ...: broken pipe标记API密钥失败: <key> (失败次数: N, 恢复时间: 5m0s/10m0s)日志证据:
关键点:上游本身没有失败(实测该模型首 token 6-11s 正常返回),纯粹是客户端主动断开,但 ccx 把它当成了上游/渠道错误。
影响
期望行为
客户端主动断开(broken pipe / 客户端侧 connection reset)不应计入 key/渠道的失败熔断统计,或至少应可配置(如
countClientCancelAsFailure: false)。这是客户端取消,不是上游故障。相关佐证
Accept: application/json(非流式解析)与"stream":truebody 并存时也容易触发X-Stainless-Read-Timeout: 1800表明 SDK 层 read 超时很长,断开是客户端应用层主动取消