Skip to content

fix(translator): drop orphan tool results on the Claude->OpenAI request path (#4385) - #4413

Merged
diegosouzapw merged 1 commit into
release/v3.8.32from
fix/4385-claude-orphan-tool
Jun 20, 2026
Merged

diegosouzapw merged 1 commit into
release/v3.8.32from
fix/4385-claude-orphan-tool

Conversation

@diegosouzapw

Copy link
Copy Markdown
Collaborator

Closes #4385

Problem

Routing a Claude-protocol conversation (/v1/messages) to an OpenAI-compatible provider (command-code, custom openai-compatible) returned a 502 from the upstream:

Messages with role 'tool' must be a response to a preceding message with 'tool_calls'

claudeToOpenAIRequest emits a role:"tool" message for every Claude tool_result block, but never drops an orphan one — a tool_result whose tool_use_id has no matching assistant.tool_calls (e.g. when history truncation / compression removed the assistant turn that issued the call but kept the result). OpenAI-compatible upstreams reject those sequences. This is why downgrading to 3.8.25 "worked" — it depends on the exact history shape the agent sends.

Fix

After fixMissingToolResponses, filter out role:"tool" messages whose tool_call_id matches no assistant.tool_calls id. This mirrors the filter already applied on the Responses→Chat path (openai-responses.ts, #2893). Placeholders inserted by fixMissingToolResponses (which DO match a tool_call) are kept; only genuine orphans are removed.

Validation (TDD)

New tests/unit/translator-claude-to-openai-orphan-tool-4385.test.ts — fails on the unfixed code (orphan survives), passes after the fix (3/3): drops an orphan, preserves a paired tool_result, and keeps valid while dropping orphan in the same turn.

One existing test (...converts tool_result blocks into tool messages...) built an unpaired tool_result (technically an orphan that produces the 502); it now pairs it with the assistant tool_call so it still exercises the content-extraction mechanics on a valid sequence — assertions on "20C"/"Thanks" are unchanged. Full translator suites green; typecheck:core + lint clean.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@diegosouzapw
diegosouzapw changed the base branch from release/v3.8.31 to release/v3.8.32 June 20, 2026 18:30
@diegosouzapw
diegosouzapw force-pushed the fix/4385-claude-orphan-tool branch from fa34314 to bf2f26d Compare June 20, 2026 19:50
@diegosouzapw
diegosouzapw merged commit 51642dc into release/v3.8.32 Jun 20, 2026
3 checks passed
@diegosouzapw
diegosouzapw deleted the fix/4385-claude-orphan-tool branch June 21, 2026 12:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant