anthropic-responses-bridge 把上游流内错误吞成空 200 SSE,Claude Code 只能报 malformed response
- Dominant language
- TypeScript
- Stars
- 2.7k
- Forks
- 401
- Avg merge
- 21h 48m
- Merged PRs (30d)
- 776
Description
**提交人**: jack
**客户端版本**: 0.0.0
---
## 现象
使用 `chatgpt/` 前缀模型(实测为 `chatgpt/gpt-5.6-sol`,走订阅直连桥 `anthropic-responses-bridge`)时,在上下文接近窗口上限的会话里发送一条大消息(单请求体约 1.49MB),turn 失败,界面只显示:
> API Error: API returned an empty or malformed response (HTTP 200) — check for a proxy or gateway intercepting the request
CLI 将其判为可重试错误,每次重试都重新上传 1.49MB 后以同样方式失败(日志实测连续失败 6+ 次)。整个过程中桥接层没有留下任何日志。
## 复现步骤
1. 新建 Claude Code 会话,选择任意 `chatgpt/` 前缀订阅直连模型;
2. 把会话上下文填到接近模型窗口上限(如 70%+);
3. 触发一个会带回大量工具结果的 turn(如一次性读取数千行大文件),使下一次 API 请求超出窗口;
4. 观察 turn 结果与 `logs/agent-*.ndjson`。
## 期望行为
- 上游拒绝请求时,用户看到真实、可操作的错误(如 context 超限);
- Claude Code 能识别 prompt too long 类错误并触发自身的自动压缩处理,而不是盲目重试;
- 桥接层对异常请求留下可排障的 warn 日志。
## 实际行为
- 桥接层给 CLI 回了一条零事件的空 200 SSE 流,CLI 只能报 “empty or malformed response (HTTP 200)”;
- 真实错误被完全掩盖,自动压缩无法触发,重试必然失败;
- 桥接层全程零日志,排障只能靠外围推断。
## 初步原因(已对照代码与本机 dev 日志验证)
日志上,每个 `upstreamBase: local-handler` 的 1.49MB 请求都对应一条 malformed 报错,且全天日志中没有 `upstream non-2xx` / `upstream fetch failed` / `upstream stream error` 任一条目——说明上游返回了 200,但流内容没有产出任何可翻译事件。对应两个代码缺口:
1. `packages/anthropic-responses-bridge/src/translate-sse.ts`:翻译器只识别 `data.type === 'response.failed' | 'error'`。OpenAI 系上游流内错误的惯用形态是 `event: error` + `data: {"error": {...}}`(data 无 `type` 字段),会落入 default 分支被静默丢弃;`event:` 行本身也完全未被解析。
2. `packages/anthropic-responses-bridge/src/handler.ts`:上游 200 后立即写 200 SSE 头,不校验 content-type;流结束时若零事件且 `messageStarted=false`,`finish()` 什么都不发,空 200 收尾且无日志。
## 建议修复方向
1. 翻译器识别 OpenAI 风格错误帧(`data.error` 无 `type`),翻译成 Anthropic `error` 事件透传错误正文;
2. 流结束时零事件 → 合成一条带上游信息的 `error` 事件,而不是空 200;
3. 上游 200 但 content-type 非 SSE、或整流零事件时记 warn 日志(含状态码、content-type、正文前缀)。
---
**OS**: win32 x64 (10.0.26200)
**界面语言**: zh-CN
Contributor guide
Research direction
Start with packages/anthropic-responses-bridge/src/translate-sse.ts and packages/anthropic-responses-bridge/src/handler.ts, then reproduce the zero-event response using the large-request scenario described in the issue. Trace both OpenAI-style error frames and the upstream 200 stream through translation and finish(). Done means errors become visible Anthropic error events, zero-event or non-SSE responses are not returned as empty 200 streams, and warn logs include the stated upstream details.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, backend-api-design, observability
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 58/100