Skip to content

[Bug] Responses ACP round 2 生成 66 字符 message id,导致 Codex 后续请求持续 400 #242

Closed

Description

@Haerbin23456

问题描述

billion-context 在 Responses API 的 ACP 多轮循环中,会为第 2 轮及后续轮次的 assistant message 重映射 ID。当前实现直接拼接:

const mapped = { id: `msg-proxy-${round}-${origId || outputIndex}`, index: outputIndex++ };

OpenAI/Codex 返回的原始 message ID 可能已经有 54 个字符;第 2 轮加上 msg-proxy-2- 后会变成 66 个字符。该 ID 被 Codex 保存到 rollout 后,下一次请求把它作为 Responses input item 回传,上游会因为超过 64 字符限制而持续返回 400,导致整条旧对话无法继续。

环境

  • billion-context: 0.1.53
  • 客户端:Codex Desktop
  • 协议:Responses API
  • ACP protocol:text

实际错误

Invalid 'input[42].id': string too long. Expected a string with maximum length 64, but got a string with length 66 instead.

实际生成的 ID:

msg-proxy-2-msg_083f5e89ab47276e016a8d80c2a5c4819780d41a8c32999fc8
  • 原始 ID 长度:54
  • msg-proxy-2- 前缀长度:12
  • 拼接后长度:66

影响

  1. 发生 ACP round 2 时,代理把 66 字符 ID 发送给客户端。
  2. Codex 将该 ID 写入本地 rollout。
  3. billion-context 的持久状态也会在 messages[].rawResponsesItem.id 中保存该 ID。
  4. 后续请求不断回放这个 ID并收到相同的 400;--passthrough 只能阻止新压缩,不能清理已经落盘的 ID。

预期行为

代理合成的 Responses item ID 应始终不超过 64 字符,同时同一轮的 output_item.added、文本 delta、content part 和 output_item.done 应继续引用同一个重映射 ID。

建议修复

对原始 ID(或 fallback output index)做确定性短哈希,同时保留 round 前缀,例如:

const mapped = {
    id: `msg-proxy-${round}-${hashId(origId || String(outputIndex))}`,
    index: outputIndex++,
};

这样示例 ID 会从 66 字符缩短为 28 字符,也不会破坏当前的生命周期引用映射。建议增加一个使用真实 54 字符原始 ID 的 round-2 回归测试,并断言最终 ID length <= 64。

Activity

  1. ranxianglei commented on Aug 25, 2026

    @ranxianglei
    Owner

    Model: qwen3.8-27b (vllm)

    Root cause found and fixed in #244.

    What happened: during multi-round compression on the Responses path, the proxy remapped upstream output items to ids of the form msg-proxy-2-<upstream-id>. With a 54-char upstream id that produces a 66-char id, over the upstream 64-char cap for input[].id. Codex Desktop persists that id into its rollout, so every subsequent request replays it and gets:

    Invalid 'input[42].id': string too long. Expected a string with maximum length 64 ...
    

    The fix (two parts):

    1. Proxy-synthesized ids now use a short deterministic digest (msg-proxy-2-<7-char-hash>, ≤ ~21 chars) — never over the cap again.
    2. Existing poisoned conversations are healed on the fly: any input[].id longer than 64 chars is rewritten to a short deterministic id (msg-fix-<hash>) before the request goes upstream, so a rollout that is currently 400-ing on every turn recovers by itself on the next request — no client-side cleanup needed.

    Will ship in the next release.

  2. ranxianglei commented on Aug 25, 2026

    @ranxianglei
    Owner

    Model: qwen3.8-27b (vllm)

    Update: the fix now lives in #243 (by @Haerbin23456 — cleaner root-cause fix reusing the existing hashId()), with the ingress-healing commit added on top. #244 closed as superseded. The four test-matrix jobs + version-guard pass on #243; build-artifact fails only due to the known fork-PR npm-secret limitation, not code.

  3. added 2 commits that reference this issue on Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions