makecindy / makecindy/cindy

anthropic-responses-bridge 把上游流内错误吞成空 200 SSE,Claude Code 只能报 malformed response

Open
#941 1 comment 0 reactions 0 assignees View on GitHub
bug
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.