Skip to content

fix(middleware): skip raw tool-call fallback when invalid view carries the same call - #4693

Open
Baldwinzc wants to merge 1 commit into
bytedance:mainfrom
Baldwinzc:fix/dangling-tool-call-raw-gating
Open

fix(middleware): skip raw tool-call fallback when invalid view carries the same call#4693
Baldwinzc wants to merge 1 commit into
bytedance:mainfrom
Baldwinzc:fix/dangling-tool-call-raw-gating

Conversation

@Baldwinzc

Copy link
Copy Markdown
Contributor

Why

DanglingToolCallMiddleware._message_tool_calls collected the raw additional_kwargs["tool_calls"] payload whenever the structured tool_calls list was empty — even when invalid_tool_calls was non-empty and carried an entry with the same id. The raw payload is a fallback serialization of the same calls: the OpenAI serializer reaches for it only once both structured views are empty, which is exactly the gating _normalize_tool_call_ids documents and implements. Collecting it alongside the invalid entry counted the call twice, and _build_patched_messages then emitted two placeholder ToolMessages for one tool_call_id — the duplicate-id shape strict OpenAI-compatible providers reject with HTTP 400, which is the failure this middleware exists to prevent.

(Defensive consistency fix: the internal gating between _message_tool_calls and _normalize_tool_call_ids disagreed; standard langchain_openai never produces this shape, but patched OpenAI-compatible providers do write raw tool_calls into additional_kwargs, so the duplicate-placeholder path is reachable in principle.)

What changed

  • The raw payload is now collected only when both structured views (tool_calls and invalid_tool_calls) are empty, matching the documented serializer behavior and the gating in _normalize_tool_call_ids. The raw-only fallback case is unchanged.

Surface area

  • Frontend UI
  • Backend API
  • Agents / LangGraphDanglingToolCallMiddleware tool-call collection
  • Sandbox
  • Skills
  • Dependencies
  • Default behavior change
  • Docs / tests / CI only

Bug fix verification

  • Test path: backend/tests/test_dangling_tool_call_middleware.py::TestBuildPatchedMessagesPatching::test_raw_fallback_is_skipped_when_invalid_view_carries_the_same_call
  • Red on main, green on this branch: yes (fails on main with 2 placeholders for call_x; passes here with exactly 1)

Validation

cd backend
PYTHONPATH=. uv run pytest tests/test_dangling_tool_call_middleware.py  # 64/64 pass
uv run ruff check / ruff format --check                                  # clean

AI assistance

Tool(s) used: Claude Code

How you used it: AI located the inconsistency during a codebase audit, wrote the fix and test; I reviewed the change and verified the red/green test run locally.

  • I've read and understand every line of this change and take responsibility for it — it's not unreviewed AI output.

…s the same call

DanglingToolCallMiddleware._message_tool_calls collected the raw
additional_kwargs tool_calls payload whenever structured tool_calls was
empty, even when invalid_tool_calls was non-empty.  The raw payload is a
fallback serialization of the SAME calls — the OpenAI serializer reaches
for it only once both structured views are empty, which is exactly the
gating _normalize_tool_call_ids documents and implements.  Collecting it
alongside a same-id invalid entry counted the call twice and emitted two
placeholder ToolMessages for one id — the duplicate-id shape strict
OpenAI-compatible providers reject with HTTP 400, the failure this
middleware exists to prevent.

Gate the raw collection on both structured views being empty, aligning
_message_tool_calls with _normalize_tool_call_ids.
@github-actions github-actions Bot added area:agents Agents, subagents, graph wiring, prompts, langgraph.json needs-validation Touches front/back contract surface; needs real-path validation risk:high High risk: backend API, agents, sandbox, auth, deps, CI size/S PR changes 20-100 lines labels Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:agents Agents, subagents, graph wiring, prompts, langgraph.json needs-validation Touches front/back contract surface; needs real-path validation risk:high High risk: backend API, agents, sandbox, auth, deps, CI size/S PR changes 20-100 lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant