fix(claude): bound Responses user to 64 chars for long metadata.user_id - #5706
giulioleone097 wants to merge 1 commit into
Conversation
Claude Code sends metadata.user_id as a ~186-char JSON string. It was copied verbatim into the Responses 'user' field, which Azure OpenAI and other OpenAI-compatible backends cap at 64 chars, so every Claude Code request routed to Azure failed with 400 "Invalid 'user': string too long". Keep short ids as-is and send the SHA-256 hex digest (already computed for prompt_cache_key) when the id exceeds 64 chars. Fixes lidge-jun#5705
|
✅ Deterministic PR hygiene checks passed. |
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: lidge-jun/opencodex/.coderabbit.yaml Review profile: ASSERTIVE Plan: Advanced Run ID: 📒 Files selected for processing (2)
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review. 📝 WalkthroughWalkthroughThe Claude inbound translation now hashes metadata user IDs longer than 64 characters before placing them in ChangesClaude request translation
Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: ⚪ Minimal · up to Long Claude user IDs are bounded in the Responses payload, while short IDs and prompt-cache keys retain their existing behavior. No actionable merge risk is established by the reviewed changes. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
⏳ DRAFT
What to do
Review readiness checklist
0/4 boxes ticked. This PR stays in draft until every box above is ticked. |
리뷰 · 우선순위 70 / 80이 PR은 Claude Code가 Azure OpenAI로 요청을 보낼 때 나는 400 에러를 고칩니다. Claude Code는 고친 곳은 tests/claude-integration/claude-inbound.test.ts:528 - 새 테스트는 메인테이너의 판단이 필요한 지점 Azure 기록에는 세션 번호 원문 대신 해시만 남습니다. 같은 입력은 항상 같은 해시라서 남용 추적은 됩니다. 원문은 해시에서 되돌릴 수 없습니다. 로그에서 세션 번호를 다시 읽어야 하면 이 방식은 안 맞습니다. 준비 체크는 0/4입니다. 작성자는 너의 추천 이 방향으로 두세요. 긴 아이디는 자르지 말고 해시하세요. 528행 테스트에 그 이 댓글은 grok-bot이 작성했습니다 |
…, #5714, #5704, #5707) (#5738) * fix(claude): bound Responses user to 64 chars for long metadata.user_id Claude Code sends metadata.user_id as a ~186-char JSON string. It was copied verbatim into the Responses 'user' field, which Azure OpenAI and other OpenAI-compatible backends cap at 64 chars, so every Claude Code request routed to Azure failed with 400 "Invalid 'user': string too long". Keep short ids as-is and send the SHA-256 hex digest (already computed for prompt_cache_key) when the id exceeds 64 chars. Fixes #5705 Co-authored-by: Giulio Leone <giulioleone097@gmail.com> * Fix plaintext V2 SSE responses with missing content type Co-authored-by: Jerry WANG <jerrywang@Jerrys-MacBook-Pro-2.local> * fix(responses): exclude dropped chat reasoning from input admission * test(admission): verify reasoning payload matches gate decisions Co-authored-by: 정우철 <oocheol@naver.com> * Allow HTTP upstream for canonical ChatGPT provider An explicit upstreamWebsocket: false now routes streaming canonical ChatGPT turns over HTTP/SSE before sending. This gives operators a supported escape from intermittent post-send WebSocket closes without replaying ambiguous turns. The default remains WebSocket and native WS controls are unavailable when HTTP is selected. Verified: focused Responses/provider suites, typecheck, structure check, privacy scan, docs build. Changed-area suite rerun pending. Co-authored-by: kosta <kosta963@gmail.com> * test(claude): pin exact user hash and the 64/65-char boundary Refs #5705 Co-authored-by: Giulio Leone <giulioleone097@gmail.com> * docs(architecture): attribute the admission fix to excluding unsent thinking Split the preserved-reasoning statement from the refusal rationale in the English and Korean paragraphs, as review of #5714 asked. Refs #5696 Co-authored-by: 정우철 <oocheol@naver.com> * fix(anthropic): count ping events as upstream liveness Anthropic streams may include any number of ping events. The adapter only turned SSE comments into heartbeats, so a long silent thinking phase that pinged was cut off at stallTimeoutSec with upstream_stall_timeout. Named and data-only ping records now yield the same heartbeat. Refs #5707 * fix(management): report upstreamWebsocket as configured in GET /api/providers The row coerced an unset value to false. After the canonical ChatGPT opt-out, unset means upstream WebSocket and false means HTTP/SSE, so a save built from the row could turn WebSocket off. The row now omits the key when it is unset. Co-authored-by: kosta <kosta963@gmail.com> * docs(providers): describe the canonical ChatGPT upstreamWebsocket opt-out The reference row, its seven translations, and the provider type and schema comments still said the canonical ChatGPT transport ignores upstreamWebsocket. It now selects HTTP/SSE when set to false. The locale rows were also behind the English row on the first-party-only restriction; they are retranslated from it. Co-authored-by: kosta <kosta963@gmail.com> * fix(responses): bound the plaintext V2 SSE prefix probe by stallTimeoutSec The carried probe read the first chunk of an unlabeled body with no deadline, before the passthrough stall guard is attached. An upstream that sent headers but no body held the request and its host lease until the client gave up, and a failed read escaped the classifier. Each probe read now races a per-read inactivity window and one total budget, both stallTimeoutSec, plus the client abort signal. Timeout, abort, and read errors cancel the reader and return the existing unsupported-content-type 502. Docs now scope the recovery to missing or unrecognized non-JSON content types. Follow-up to #5683 review (maintainer, Codex, CodeRabbit). Co-authored-by: Jerry WANG <jerrywang@Jerrys-MacBook-Pro-2.local> --------- Co-authored-by: Giulio Leone <giulioleone097@gmail.com> Co-authored-by: Jerry WANG <jerrywang@Jerrys-MacBook-Pro-2.local> Co-authored-by: 정우철 <oocheol@naver.com> Co-authored-by: kosta <kosta963@gmail.com>
Summary
Fixes #5705.
Claude Code sends
metadata.user_idas a JSON string of about 186 characters.anthropicToResponsesTranslationcopied it verbatim into the Responsesuserfield. Azure OpenAI, like other OpenAI-compatible Responses backends, capsuserat 64 characters, so every Claude Code or Claude Desktop Code-tab request routed to an Azure model failed:Change in
src/claude/inbound.ts:user-abcassertion stay as they are.prompt_cache_key, so the change computes it once and reuses it.prompt_cache_keyvalues are unchanged.Verification
bun test tests/claude-integration/: 778 pass, 2 fail. The two failures are in the intercept local-CA tests ("CA certificate is a self-signed X.509 v3 authority" and "leaf is issued by the CA and names every requested host in SAN"). They fail the same way on unmodifieddev(782bfb8) in this environment and are unrelated to this change.tests/claude-integration/claude-inbound.test.ts: ametadata.user_idlonger than 64 characters produces a 64-character hexuser, andprompt_cache_keymatches its first 32 characters.bun run typecheck(tsc --noEmit): clean.claude -p --model <slot>returnsOKforazure-openai/gpt-6-astra,azure-openai/gpt-5.6-solandnativegpt-5.6-sol. Before the patch the Azure slots returned the 400 above.Checklist
Review readiness checklist
This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:
Required local validation passed; commands, results, and any full-suite exception are documented.
I pushed my PR to a recent dev commit (at most 10 behind; a maintainer may still ask for the exact tip before merge).
I resolved all correct Codex and CodeRabbit findings.
My PR is ready for review.
Summary by CodeRabbit