Description
Responses from the proxy carry no request-id header. Claude Code populates the requestId field of every transcript record from that header, so records written during a proxied session have no request id at all.
Measured on one proxied session against one native Anthropic session on the same machine:
| transcript |
usage-bearing records |
with requestId |
proxied (gpt-5.6-sol) |
4 |
0 |
| native Anthropic |
756 |
753 |
A transcript legitimately repeats a record, and consumers de-duplicate those repeats by request id. With the field empty they cannot, and totals double. Claude Code's own parse_session.py --cache reported the same 2-request session as:
gpt-5.6-sol (4 turns)
input total: 90,482 fresh: 47,986 cache_read: 42,496
against true figures of 2 turns and 23,993 fresh input tokens. Its requestId column shows synthetic (no-req-L15) placeholders for every row.
The response body does carry a unique id (msg_<32 hex>), and for non-streaming responses that value is usable as a substitute key. It is not available in headers for a streaming response, where the id arrives inside the message_start event rather than alongside the response.
Expected Behavior
Every response carries a request-id header holding a value that is stable for one upstream request and distinct across requests, for streaming and non-streaming alike, so Claude Code records a usable requestId and consumers can de-duplicate transcript records.
Reproduction Steps
- Start the proxy:
CCP_ALIAS_PROVIDER=codex claude-code-proxy serve --port 18765 --no-monitor.
- POST an Anthropic-shaped request to
http://localhost:18765/v1/messages with a small max_tokens and inspect the response headers: they contain content-type, transfer-encoding and date, with no request-id.
- Run any Claude Code session against the proxy with
ANTHROPIC_BASE_URL=http://localhost:18765.
- Inspect the resulting transcript JSONL and observe no record carries a
requestId field.
Environment / Context
claude-code-proxy 0.1.32 (release build, linux-amd64), Claude Code 2.1.226, Linux, codex provider, observed 2026-08-10.
Discovered During
Investigating why per-session token totals from a proxied Claude Code session were exactly double the true values.
Suggested Fix
Unverified. dispatch_request already mints a per-request req_id and threads it through logging and the monitor, and RequestMonitorGuard carries it; it simply never reaches the response. Every response path in src/server.rs funnels through monitor_response_body, which destructures the response parts before the body is streamed, so stamping the header there would cover both streaming and non-streaming responses in one place. Preserving an upstream-supplied request-id rather than overwriting it would avoid relabelling a real provider id with a local uuid.
Description
Responses from the proxy carry no
request-idheader. Claude Code populates therequestIdfield of every transcript record from that header, so records written during a proxied session have no request id at all.Measured on one proxied session against one native Anthropic session on the same machine:
requestIdgpt-5.6-sol)A transcript legitimately repeats a record, and consumers de-duplicate those repeats by request id. With the field empty they cannot, and totals double. Claude Code's own
parse_session.py --cachereported the same 2-request session as:against true figures of 2 turns and 23,993 fresh input tokens. Its
requestIdcolumn shows synthetic(no-req-L15)placeholders for every row.The response body does carry a unique
id(msg_<32 hex>), and for non-streaming responses that value is usable as a substitute key. It is not available in headers for a streaming response, where the id arrives inside themessage_startevent rather than alongside the response.Expected Behavior
Every response carries a
request-idheader holding a value that is stable for one upstream request and distinct across requests, for streaming and non-streaming alike, so Claude Code records a usablerequestIdand consumers can de-duplicate transcript records.Reproduction Steps
CCP_ALIAS_PROVIDER=codex claude-code-proxy serve --port 18765 --no-monitor.http://localhost:18765/v1/messageswith a smallmax_tokensand inspect the response headers: they containcontent-type,transfer-encodinganddate, with norequest-id.ANTHROPIC_BASE_URL=http://localhost:18765.requestIdfield.Environment / Context
claude-code-proxy 0.1.32(release build, linux-amd64), Claude Code 2.1.226, Linux, codex provider, observed 2026-08-10.Discovered During
Investigating why per-session token totals from a proxied Claude Code session were exactly double the true values.
Suggested Fix
Unverified.
dispatch_requestalready mints a per-requestreq_idand threads it through logging and the monitor, andRequestMonitorGuardcarries it; it simply never reaches the response. Every response path insrc/server.rsfunnels throughmonitor_response_body, which destructures the response parts before the body is streamed, so stamping the header there would cover both streaming and non-streaming responses in one place. Preserving an upstream-suppliedrequest-idrather than overwriting it would avoid relabelling a real provider id with a local uuid.