Skip to content

fix: keep proxy-synthesized Responses item ids within the 64-char upstream cap (#242) - #244

Closed
ranxianglei wants to merge 1 commit into
masterfrom
2026-08-25_responses-id-length
Closed

ranxianglei wants to merge 1 commit into
masterfrom
2026-08-25_responses-id-length

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

Model: qwen3.8-27b (vllm)

Fixes #242.

Root cause

When the Responses-API compress loop runs round 2+, src/loop/adapter-responses.ts remapped upstream items with:

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

origId is the full upstream message id (e.g. 54 chars from Codex/OpenAI). With the msg-proxy-2- prefix that is 66 chars — over the upstream 64-char limit for input[].id. Codex Desktop persists that id into its rollout; on every subsequent request the rollout replays it and upstream rejects:

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

--passthrough does not help (the poisoned id is already persisted client-side). Affected: v0.1.53, Responses path (Codex).

Fix (two parts)

1. Stop minting over-long ids — the round-2+ mapping now uses a deterministic FNV-1a base36 digest instead of embedding the upstream id:

const mapped = { id: `msg-proxy-${round}-${shortItemHash(origId || String(outputIndex))}`, ... }

≤ ~21 chars. All lifecycle events (output_item.added / output_text.delta / output_item.done) share the same mapped id, so references stay intact.

2. Heal already-poisoned rollouts — prepareResponses now runs sanitizeResponsesInputIds(parsed.input) (new export) before the projection reads the input: any input[].id longer than 64 chars is rewritten in place to msg-fix-${shortItemHash(id)} — deterministic and idempotent, so repeated requests keep referencing the same replacement id. Conversations that were permanently 400-ing recover on their next request.

Not changed: /responses/compact (native compaction never carries these ids — a poisoned rollout 400s on its first regular request before compact could run), call_id values, and the ~27-char msg-proxy-${Date.now()}-… ids used by text/tool/marker emission (already within the cap).

Tests

  • New round-2 lifecycle test using the exact 54-char upstream id from the issue: asserts the mapped id is ≤ 64 chars and that deltas + done events reference the same mapped id.
  • New unit tests for sanitizeResponsesInputIds: short ids untouched, deterministic (same source → same replacement), call_id untouched, idempotent on re-run, non-array tolerated.
  • Existing round-2 lifecycle test (msg-proxy-2- prefix assertion) still passes.
typecheck ✅  tests 595/595 ✅  build ✅

…tream cap (#242)

- round-2 message id remap: deterministic short hash instead of embedding the
  full upstream id (66 chars → ~21)
- sanitizeResponsesInputIds: heal already-poisoned rollouts at request ingress
  (deterministic msg-fix-<hash> rewrite, stable across requests)
- regression tests with the exact 54-char id from the bug report
@github-actions

Copy link
Copy Markdown
Contributor

📦 Built Package Artifact

Branch: 2026-08-25_responses-id-length (3abbab5)

Option A — Install from npm PR tag (recommended)

npm install -g billion-context@pr-244

Each push to this PR publishes a new version under the pr-244 npm tag.

Option B — Download artifact

  1. Download the artifact from the Actions run
  2. Extract the tarball and install:
tar xzf billion-context-pr244.tgz
npm install -g package

This comment is automatically updated on each push.

@ranxianglei

Copy link
Copy Markdown
Owner Author

Superseded by #243 — same root-cause fix (that one cleaner: reuses the existing hashId()), plus the ingress-healing commit now pushed onto #243's branch. Keeping the contributor's PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant