litellm - 💡(How to fix) Fix [Bug]: Bedrock toolUse _N split creates orphan toolResults on subsequent turns [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
BerriAI/litellm#26060Fetched 2026-04-19 15:06:03
View on GitHub
Comments
0
Participants
1
Timeline
1
Reactions
0
Author
Participants
Timeline (top)
labeled ×1

Error Message

BedrockException: {"message":"Expected toolResult blocks at messages.N.content for the following Ids: tooluse_<orig_id>_1"}

Root Cause

  • #20543 (closed by #19198 — the workaround that caused this)
  • #18667 (predecessor, closed prematurely)
  • #7099 (different root cause, same symptom class)
  • openai-agents-python#2061 / PR #2337 (upstream tried to neutralize malformed JSON at the SDK level; litellm's split undoes it on the wire)

Fix Action

Fix / Workaround

#19198 added a split-on-concatenated-JSON workaround (fixing #20543 / #18667) in _convert_to_bedrock_tool_call_invoke (factory.py ~L3815):

  • #20543 (closed by #19198 — the workaround that caused this)
  • #18667 (predecessor, closed prematurely)
  • #7099 (different root cause, same symptom class)
  • openai-agents-python#2061 / PR #2337 (upstream tried to neutralize malformed JSON at the SDK level; litellm's split undoes it on the wire)

Code Example

parsed_objects = split_concatenated_json_objects(arguments)
if parsed_objects:
    for obj_idx, obj in enumerate(parsed_objects):
        block_id = tool_id if obj_idx == 0 else f"{tool_id}_{obj_idx}"
        bedrock_tool = BedrockToolUseBlock(input=obj, name=name, toolUseId=block_id)

---

BedrockException: {"message":"Expected toolResult blocks at messages.N.content for the following Ids: tooluse_<orig_id>_1"}

---

litellm.llms.bedrock.common_utils.BedrockError:
  {"message":"Expected toolResult blocks at messages.328.content
   for the following Ids: tooluse_<redacted>_1"}
RAW_BUFFERClick to expand / collapse

Check for existing issues

  • I have searched the existing issues and checked that my issue is not a duplicate.

What happened?

#19198 added a split-on-concatenated-JSON workaround (fixing #20543 / #18667) in _convert_to_bedrock_tool_call_invoke (factory.py ~L3815):

parsed_objects = split_concatenated_json_objects(arguments)
if parsed_objects:
    for obj_idx, obj in enumerate(parsed_objects):
        block_id = tool_id if obj_idx == 0 else f"{tool_id}_{obj_idx}"
        bedrock_tool = BedrockToolUseBlock(input=obj, name=name, toolUseId=block_id)

This emits N toolUse blocks from one OpenAI tool_call. However _convert_to_bedrock_tool_call_result (factory.py ~L3858) still emits exactly one toolResult per OpenAI tool_call_id. On the next turn of a multi-turn conversation, the outgoing Bedrock request therefore advertises toolUse ids ABC, ABC_1, ABC_2, … but only supplies a toolResult for ABC. Bedrock rejects:

BedrockException: {"message":"Expected toolResult blocks at messages.N.content for the following Ids: tooluse_<orig_id>_1"}

Retrying does not help — the session history is permanently split-asymmetric and every subsequent call hits the same 400.

Observed

  • litellm==1.82.2
  • openai-agents==0.12.4
  • Model: global.anthropic.claude-opus-4-6-v1
  • Bedrock Converse streaming endpoint, region us-east-2.
  • Repro: long-running agentic loop where Claude Opus 4.6 emits concatenated JSON in a single tool_use.input. One such emission terminates the session permanently on the following turn.

The _N-suffix split code is still identical on main (verified 2026-04-19: factory.py:3872-3877).

Stack trace excerpt

litellm.llms.bedrock.common_utils.BedrockError:
  {"message":"Expected toolResult blocks at messages.328.content
   for the following Ids: tooluse_<redacted>_1"}

Expected behavior

One of:

  1. Symmetric split — when the send-side splits a malformed tool_call into N toolUse blocks, the result-side should duplicate the matching tool_result across the same N toolUseIds (or emit a synthetic "no result, split sibling" result for ids 1..N-1).
  2. Skip the split on replay — only split on the first emission, and on subsequent turns coalesce back to one toolUse with input equal to the concatenation.
  3. Gate behind a flag — agent frameworks (openai-agents, agno, langchain-aws) already surface parse errors as tool outputs; they'd rather see the raw malformed args and handle it themselves than get silently resurrected as orphan siblings.

Option 1 is probably least disruptive — preserves the #20543 fix and makes the round-trip symmetric.

Related

  • #20543 (closed by #19198 — the workaround that caused this)
  • #18667 (predecessor, closed prematurely)
  • #7099 (different root cause, same symptom class)
  • openai-agents-python#2061 / PR #2337 (upstream tried to neutralize malformed JSON at the SDK level; litellm's split undoes it on the wire)

What LiteLLM version are you on?

v1.82.2 — confirmed same code on main as of 2026-04-19.

extent analysis

TL;DR

To fix the issue, modify the _convert_to_bedrock_tool_call_result function to emit multiple toolResult blocks, one for each toolUse block, to maintain symmetry in the round-trip.

Guidance

  • Identify the _convert_to_bedrock_tool_call_result function in factory.py and modify it to duplicate the toolResult for each toolUseId generated by the split-on-concatenated-JSON workaround.
  • Verify that the modified function correctly handles the case where a single tool_call is split into multiple toolUse blocks and ensures that each toolUseId has a corresponding toolResult.
  • Consider adding a flag to gate the split-on-concatenated-JSON workaround behind a configurable option, allowing agent frameworks to handle malformed JSON inputs themselves.
  • Review related issues (#20543, #18667, #7099, and openai-agents-python#2061) to ensure that the fix does not introduce regressions.

Example

def _convert_to_bedrock_tool_call_result(...):
    # ...
    if parsed_objects:
        tool_results = []
        for obj_idx, obj in enumerate(parsed_objects):
            block_id = tool_id if obj_idx == 0 else f"{tool_id}_{obj_idx}"
            tool_result = BedrockToolResult(..., toolUseId=block_id)
            tool_results.append(tool_result)
        return tool_results
    # ...

Notes

The fix assumes that the _convert_to_bedrock_tool_call_result function is the correct place to modify to achieve symmetry in the round-trip. However, the actual implementation may vary depending on the specific requirements and constraints of the system.

Recommendation

Apply the workaround by modifying the _convert_to_bedrock_tool_call_result function to emit multiple toolResult blocks, as this approach preserves the fix for #20543 and makes the round-trip symmetric.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

One of:

  1. Symmetric split — when the send-side splits a malformed tool_call into N toolUse blocks, the result-side should duplicate the matching tool_result across the same N toolUseIds (or emit a synthetic "no result, split sibling" result for ids 1..N-1).
  2. Skip the split on replay — only split on the first emission, and on subsequent turns coalesce back to one toolUse with input equal to the concatenation.
  3. Gate behind a flag — agent frameworks (openai-agents, agno, langchain-aws) already surface parse errors as tool outputs; they'd rather see the raw malformed args and handle it themselves than get silently resurrected as orphan siblings.

Option 1 is probably least disruptive — preserves the #20543 fix and makes the round-trip symmetric.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

litellm - 💡(How to fix) Fix [Bug]: Bedrock toolUse _N split creates orphan toolResults on subsequent turns [1 participants]