Codex Responses→Chat Completions proxy forwards tool_choice without tools, causing strict upstream 503#3640
Merged
farion1231 merged 1 commit intoJun 4, 2026
Conversation
…nversion Strict OpenAI-compatible upstreams (vLLM, enterprise gateways) reject requests that carry tool_choice or parallel_tool_calls without a non-empty tools array, returning 503/400 with: "When using `tool_choice`, `tools` must be set." The Responses→Chat Completions converter unconditionally forwarded tool_choice and parallel_tool_calls even when tools ended up absent or empty after conversion. This commit adds a guard that removes both fields when the resulting tools array is missing or empty. Added 9 regression tests covering: - tool_choice dropped when tools absent - tool_choice dropped when tools is empty array - parallel_tool_calls dropped when tools absent - tool_choice dropped when all tools filtered (e.g., missing name) - tool_choice preserved when tools present (auto + function type) - clean output when neither tool_choice nor tools present - tool_choice 'none' dropped when no tools - tool_search_output providing tools keeps tool_choice Refs farion1231#3557
Owner
|
@codex review |
|
Codex Review: Didn't find any major issues. More of your lovely PRs please. ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
farion1231
approved these changes
Jun 4, 2026
farion1231
left a comment
Owner
There was a problem hiding this comment.
Thank you for your contribution!
AnXiYiZhi
pushed a commit
to AnXiYiZhi/DevCLaw-old
that referenced
this pull request
Jun 9, 2026
…nversion (farion1231#3640) Strict OpenAI-compatible upstreams (vLLM, enterprise gateways) reject requests that carry tool_choice or parallel_tool_calls without a non-empty tools array, returning 503/400 with: "When using `tool_choice`, `tools` must be set." The Responses→Chat Completions converter unconditionally forwarded tool_choice and parallel_tool_calls even when tools ended up absent or empty after conversion. This commit adds a guard that removes both fields when the resulting tools array is missing or empty. Added 9 regression tests covering: - tool_choice dropped when tools absent - tool_choice dropped when tools is empty array - parallel_tool_calls dropped when tools absent - tool_choice dropped when all tools filtered (e.g., missing name) - tool_choice preserved when tools present (auto + function type) - clean output when neither tool_choice nor tools present - tool_choice 'none' dropped when no tools - tool_search_output providing tools keeps tool_choice Refs farion1231#3557 Co-authored-by: yueqi.guo <[email protected]>
gfunc
pushed a commit
to gfunc/cc-switch
that referenced
this pull request
Jun 19, 2026
…nversion (farion1231#3640) Strict OpenAI-compatible upstreams (vLLM, enterprise gateways) reject requests that carry tool_choice or parallel_tool_calls without a non-empty tools array, returning 503/400 with: "When using `tool_choice`, `tools` must be set." The Responses→Chat Completions converter unconditionally forwarded tool_choice and parallel_tool_calls even when tools ended up absent or empty after conversion. This commit adds a guard that removes both fields when the resulting tools array is missing or empty. Added 9 regression tests covering: - tool_choice dropped when tools absent - tool_choice dropped when tools is empty array - parallel_tool_calls dropped when tools absent - tool_choice dropped when all tools filtered (e.g., missing name) - tool_choice preserved when tools present (auto + function type) - clean output when neither tool_choice nor tools present - tool_choice 'none' dropped when no tools - tool_search_output providing tools keeps tool_choice Refs farion1231#3557 Co-authored-by: yueqi.guo <[email protected]>
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.
Self Checks / 自检
Related App / 涉及应用
Codex
Problem or Motivation / 问题或动机
When a user configures a third-party provider that only supports the OpenAI Chat Completions API for Codex, CC Switch's proxy layer converts Codex's Responses API requests into Chat Completions requests before forwarding them upstream.
The request flow is:
The problem: during this Responses→Chat Completions conversion,
tool_choiceis unconditionally forwarded even when thetoolsarray is absent or empty in the converted request. Strict OpenAI-compatible upstreams (vLLM, enterprise gateways, OneAPI instances, etc.) reject this field combination.Root Cause / 根因分析
In
src-tauri/src/proxy/providers/transform_codex_chat.rs, the functionresponses_to_chat_completions_with_reasoning()performs the request conversion. The relevant field handling:tools— only written to the output when the converted tool list is non-empty (lines 309-311). If no valid tools survive conversion (e.g., missing name, unsupported type, empty namespace), the field is omitted entirely.tool_choice— unconditionally copied from the original Responses request whenever it is present (lines 313-314).parallel_tool_calls— forwarded as a passthrough field viaEXTRA_CHAT_PASSTHROUGH_FIELDS(lines 317-321).These three fields are handled independently with no constraint that
tool_choiceandparallel_tool_callsrequire a non-emptytoolsarray.Triggering conditions (any one of the following):
tool_choicein the Responses request, buttoolsis absent or emptytoolsexists in the request but all entries are filtered out during conversion (e.g., function tool with no name, namespace tool with no children)tool_search_outputininputprovides no loadable tools, yettool_choicewas set in the original requestFull Error Message / 完整报错
Upstream Reproduction / 上游复现
Sending a Chat Completions request directly to a strict upstream with
tool_choicebut withouttoolsreproduces the same rejection:Proposed Solution / 建议方案
After forwarding all passthrough fields, add a guard: if
toolsis absent or empty in the converted request, removetool_choiceandparallel_tool_calls.Why not send
"tools": []? Some strict upstreams also reject an emptytoolsarray whentool_choiceis present. Removingtool_choiceentirely is the safest approach.Changes Made / 已实施的改动
File:
src-tauri/src/proxy/providers/transform_codex_chat.rsEXTRA_CHAT_PASSTHROUGH_FIELDSloop that dropstool_choiceandparallel_tool_callswhentoolsis absent or emptytool_choicedropped whentoolsis absenttool_choicedropped whentoolsis an empty arrayparallel_tool_callsdropped whentoolsis absenttool_choicedropped when all tools are filtered out during conversion (e.g., function tool with no name)tool_choicepreserved whentoolsis present and non-emptytool_choicepreserved and correctly name-mapped whentoolsis presenttool_choicenortoolsare present in the original requesttool_choice: "none"also dropped when no tools (strict upstreams reject anytool_choicevalue withouttools)tool_search_outputininputproviding tools keepstool_choiceintactTest Results / 测试结果
Full test suite: 1426 passed, 0 failed
Code formatting:
cargo fmt --checkpassedEnvironment / 环境
693c3872)openai_chat)Related Issues / 相关 Issue
tool_choice/toolsguard bug)Additional Context / 补充信息
This bug affects any strict OpenAI-compatible upstream provider (vLLM, enterprise gateways, OneAPI, LiteLLM, etc.) when used as a Chat Completions backend for Codex through CC Switch's local routing. The fix is minimal — it only removes fields that would cause upstream rejection, without altering the conversion logic for requests that already have valid tools.