You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
因为 created 已提前发出,响应头已经提交为 200,状态码不可能再改回 429 —— 直接沿用原来的 sendResponsesError(res, 429, ...) 只会抛 Cannot write headers after they are sent to the client,客户端收到的是一个内部 error 事件(实测):
event: response.created
event: response.in_progress
event: error
data: {"type":"error","sequence_number":2,"code":null,"message":"Cannot write headers after they are sent to the client",...}
Activity
xelr233 commentedon Oct 2, 2026
已确认并在 #57 提交修复,感谢这份如此详尽的报告 —— 根因定位、
ERR_HTTP_HEADERS_SENT的坑、res.end()漏收尾三个点全部命中,修复方案就是按你给出的口径实现的。复核补充两点:
根因确认:
#54之后translator.start()在收到上游 200 时被无条件提前调用(治首字前 15~40s 静默被 nginx/CDN 掐连接),createdSent自此恒为true,「outputTokens === 0 && !translator.started」成了死代码。同一 mock 场景下实测与你的表格一致:cce214d 返回 429,ce5a217 起返回 200 +response.completed(output: [])谎报成功。修复口径(fix: 流式 /v1/responses 零输出防护失效(#56,#54 回归)—— 空响应不再谎报 response.completed #57):
get hasOutput()(outputIndex > 0 || doneItems.length > 0,即「是否真的产出过 output item」),替代恒真的!translator.started;sendResponsesError(会抛ERR_HTTP_HEADERS_SENT),按本文件既有失败口径translator.fail(...)写response.failed(status:"failed"、error.code:"upstream_error"、message 沿用Empty response from upstream (zero output tokens));!started → 429子分支保留兜底;res.end()—— 你指出的「return会跳过流式分支尾部收尾、客户端挂在永不结束的 SSE 上」已按此处理;#54波及(按fullText/thinkingText/toolCalls判空),保持 429 不动。验证:新增
test/responses-zero-output.test.mjs三个用例(流式空响应 →response.failed且无response.completed、正常输出不受影响、非流式仍 429),并在修复前的代码上做了反向验证 —— 用例如预期红,证明能抓住这个回归;相邻套件 36/36 全绿。全量矩阵(node 22 / node 24 / bun)在 #57 的 CI 已通过。另外你的本机补丁与 #57 行为一致(200 +
response.failed、无内部错误事件、正常收尾),可以直接沿用,也可以等 #57 合并后同步。fix: 流式 /v1/responses 零输出防护失效(#56,#54 回归)——空响应不再谎报 completed
Merge upstream/master into master(同步上游 MAXeaglet#29/MAXeaglet#31/MAXe…