Bug Description
When Codex (v0.118.0+) uses the Responses API via WebSocket through CLIProxyAPI, all multi-turn tool calls fail with:
400: No tool call found for function call output with call_id call_xxx
Root Cause (confirmed via logs)
CLIProxyAPI's WebSocket → HTTP translation does not chain previous_response_id between turns.
From proxy-config.yaml request logs:
Turn 1 (prewarm): previous_response_id: "resp_prewarm_5c1ef71b-..." ✅
Turn 2 (actual): previous_response_id: null ❌
Turn 3: previous_response_id: null ❌
Turn 4: previous_response_id: null ❌
What should happen: each turn must reference the response_id returned by the previous turn. OpenAI uses this to look up the tool calls from the previous response. When null is sent, OpenAI starts a fresh conversation with no tool call context → rejects the function_call_output.
Timeline
- Before Codex v0.118.0: Codex used Chat Completions API (stateless) → round-robin worked fine, no
previous_response_id needed
- After Codex v0.118.0: Codex switched to Responses API over WebSocket (
Openai-Beta: responses_websockets=2026-02-06) → stateful, previous_response_id must be chained between turns
Evidence
Log header from Codex request:
Openai-Beta: responses_websockets=2026-02-06
Version: 0.118.0
Log showing null chaining (from v1-responses-*.log):
{"previous_response_id": null, "model": "gpt-5.3-codex", ...} // turn 2, 3, 4
Error in main.log (source: openai_responses_websocket.go:632):
responses websocket: downstream_out event=error payload={"type":"error","status":400,
"message":"No tool call found for function call output with call_id call_xxx"}
Expected Fix
In openai_responses_websocket.go, when proxying WebSocket → HTTP:
- Track the
response_id returned by each OpenAI HTTP response
- Use it as
previous_response_id in the next HTTP request for the same WebSocket session
Workaround
Remove openai_base_url from Codex config to bypass CLIProxyAPI and connect directly to OpenAI.
Related: #2594 (session-sticky-round-robin feature request)
Bug Description
When Codex (v0.118.0+) uses the Responses API via WebSocket through CLIProxyAPI, all multi-turn tool calls fail with:
Root Cause (confirmed via logs)
CLIProxyAPI's WebSocket → HTTP translation does not chain
previous_response_idbetween turns.From
proxy-config.yamlrequest logs:What should happen: each turn must reference the
response_idreturned by the previous turn. OpenAI uses this to look up the tool calls from the previous response. Whennullis sent, OpenAI starts a fresh conversation with no tool call context → rejects thefunction_call_output.Timeline
previous_response_idneededOpenai-Beta: responses_websockets=2026-02-06) → stateful,previous_response_idmust be chained between turnsEvidence
Log header from Codex request:
Log showing null chaining (from
v1-responses-*.log):{"previous_response_id": null, "model": "gpt-5.3-codex", ...} // turn 2, 3, 4Error in
main.log(source:openai_responses_websocket.go:632):Expected Fix
In
openai_responses_websocket.go, when proxying WebSocket → HTTP:response_idreturned by each OpenAI HTTP responseprevious_response_idin the next HTTP request for the same WebSocket sessionWorkaround
Remove
openai_base_urlfrom Codex config to bypass CLIProxyAPI and connect directly to OpenAI.Related: #2594 (session-sticky-round-robin feature request)