Repository navigation
fix(proxy): emit response.incomplete for incomplete Codex responses - #551
Merged
SaladDay merged 1 commit intoNov 5, 2026
Merged
Conversation
Converted Anthropic Messages and Chat Completions streams already set response.status to "incomplete" (max_tokens, refusal, truncation, finish_reason=length), but always sent the terminal event as response.completed. Codex only reads incomplete_details from a response.incomplete event, so these responses were treated as normal completions. Pick the terminal event name from the response status, and let the usage parser, Codex OAuth SSE aggregation and Chat history recorder accept response.incomplete so usage and history are not lost. Fixes SaladDay#546 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RGB-loop
marked this pull request as ready for review
November 5, 2026 07:13
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When a Codex request is converted to Anthropic Messages or Chat Completions, a response that stops early (
max_tokens,length, refusal, truncated stream) getsstatus: "incomplete", but the terminal SSE event is alwaysresponse.completed. Codex (rust-v0.162.1,codex-api/src/sse/responses.rs) only readsincomplete_detailson aresponse.incompleteevent, so it treats these turns as successful. The existing code comments say the status is there so Codex does not take truncation or refusal for a normal completion; that intent was not being met.codex_responses_sse::response_terminalpicks the event name andtypefromresponse.status. Both converters use it.handlers.rs) and the Chat history recorder now acceptresponse.incomplete. Without this, the converter change would drop usage and Chat history for these responses.This also fixes two of these consumers for native Responses upstreams, which already send
response.incomplete: usage was lost, and Codex OAuth aggregation failed withNo response.completed event in upstream SSE.Behavior change for Codex users
Turns that used to end silently with a truncated result now end with an error, matching native OpenAI behavior:
max_output_tokens:Incomplete response returned, reason: max_output_tokenscontent_filter): Codex's content filter errorThis may show up more often than with native OpenAI. On Codex → Anthropic routes, when neither the request nor the provider sets
max_output_tokens, the proxy injectsmax_tokens=8192(request_builder.rs), so long outputs can hit the limit. Users can raise it with the provider'smeta.max_output_tokens. This PR does not change that default.Codex may retry on this stream error, which would replay the same limit and use more tokens. I did not confirm its retry policy.
Not changed: Chat
finish_reason=content_filterstill maps tocompleted, and truncated streams reportmax_output_tokenson the Anthropic path butstream_truncatedon the Chat path.Testing
max_tokens/length, refusal, truncation), the usage parser, OAuth aggregation and Chat history. All 7 fail with the old behavior restored and pass with this change.cargo fmt --check,cargo test --lib proxy:: -- --skip services::proxyandcargo test --test proxy_claude_streamingpass on Rust 1.95.services::proxytests fail on temp HOME races, also with the change reverted. With--test-threads=1they pass 77/77.Fixes #546
🤖 Generated with Claude Code