crewai - ✅(Solved) Fix [BUG] AWS Bedrock ValidationException with multiple tool calls - toolResult blocks not grouped [4 pull requests, 1 comments, 2 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
crewAIInc/crewAI#4749Fetched 2026-04-08 00:40:25
View on GitHub
Comments
1
Participants
2
Timeline
12
Reactions
0
Timeline (top)
cross-referenced ×4referenced ×4assigned ×1closed ×1

When an AWS Bedrock model makes multiple tool calls in a single response, CrewAI sends each tool result as a separate user message. Bedrock's Converse API requires all tool results for a given assistant message to be grouped together in a single user message, causing a ValidationException.

The bug is in lib/crewai/src/crewai/llms/providers/bedrock/completion.py (~line 1800) where tool messages are converted one at a time without buffering consecutive tool results.

Error Message

ValidationException: Expected toolResult blocks at messages.2.content for the following Ids: tooluse_xxx, tooluse_yyy

Root Cause

This bug only affects AWS Bedrock models. OpenAI and Ollama models work correctly because they don't have the same message structure requirements.

Fix Action

Fixed

PR fix notes

PR #4751: fix(bedrock): group consecutive tool results into single user message

Description (problem / solution / changelog)

Summary

Fixes #4749 — When a Bedrock model makes multiple parallel tool calls in a single response, CrewAI was sending each toolResult as a separate user message. Bedrock's Converse API requires all tool results for a given assistant turn to be in one user message, causing a ValidationException.

The fix modifies _format_messages_for_converse in bedrock/completion.py to detect consecutive tool-role messages and append their toolResult blocks to the same user message instead of creating separate ones.

Before (broken):

Message 0: user (prompt)
Message 1: assistant (toolUse A, toolUse B)
Message 2: user (toolResult A)       ← separate
Message 3: user (toolResult B)       ← causes ValidationException

After (fixed):

Message 0: user (prompt)
Message 1: assistant (toolUse A, toolUse B)
Message 2: user (toolResult A, toolResult B)  ← grouped

Review & Testing Checklist for Human

  • Verify the grouping condition (~line 1829-1835) won't accidentally merge tool results across different assistant turns — the test_bedrock_multi_turn_tool_results_not_merged_across_turns test covers this, but worth a manual read
  • Confirm the fix works end-to-end with a real Bedrock model that issues parallel tool calls (unit tests only cover the message formatting layer, not actual API interaction)
  • Check that _format_messages_for_converse is indeed the single formatting entry point for all Bedrock call paths (converse, streaming, async) — if so, this one-location fix is sufficient

Notes

<!-- CURSOR_SUMMARY -->

[!NOTE] Medium Risk Touches Bedrock Converse message formatting for tool calling, which can affect all tool-call conversations if grouping logic misfires. Added unit tests reduce risk, but real API contract edge cases could still surface.

Overview Fixes Bedrock Converse message formatting so consecutive tool role messages append their toolResult blocks into one user message, matching Bedrock’s requirement for parallel tool calls and preventing ValidationException errors.

Adds targeted tests to validate grouping for 2+ parallel tool results, keep single-tool behavior unchanged, and ensure tool results are not merged across separate assistant turns.

<sup>Written by Cursor Bugbot for commit b125e572c4932f3e50d3e247bd2c552e831d9240. This will update automatically on new commits. Configure here.</sup>

<!-- /CURSOR_SUMMARY -->

Changed files

  • lib/crewai/src/crewai/llms/providers/bedrock/completion.py (modified, +24/-12)
  • lib/crewai/tests/llms/bedrock/test_bedrock.py (modified, +187/-0)

PR #4766: fix: group multiple Bedrock tool results into single user message

Description (problem / solution / changelog)

Problem

When a Bedrock model makes multiple tool calls in a single response, CrewAI sends each tool result as a separate user message:

Message 0: user (prompt)
Message 1: assistant (toolUse A, toolUse B)
Message 2: user (toolResult A)  ← Separate message
Message 3: user (toolResult B)  ← Separate message — causes ValidationException

Bedrock's Converse API requires all tool results for a given assistant message to be grouped in one user message.

Solution

When converting tool messages to Bedrock format, check if the previous converse message is already a user message containing toolResult blocks. If so, append the new toolResult to it instead of creating a new message:

Message 0: user (prompt)
Message 1: assistant (toolUse A, toolUse B)
Message 2: user (toolResult A, toolResult B)  ← Grouped correctly

Fixes #4749

<!-- CURSOR_SUMMARY -->

[!NOTE] Medium Risk Moderate risk because it changes how tool responses are serialized into the Bedrock conversation history, which can affect multi-tool-call flows and message alternation edge cases.

Overview Prevents Bedrock Converse ValidationException when a model returns multiple tool calls in one assistant turn by grouping all corresponding toolResult blocks into a single subsequent user message.

Updates _format_messages_for_converse so consecutive tool role messages append their toolResult blocks onto the previous user message (when it already contains tool results) instead of emitting multiple separate user turns.

<sup>Written by Cursor Bugbot for commit d3840574252e41735ba5d125fc54a20bd3a6496d. This will update automatically on new commits. Configure here.</sup>

<!-- /CURSOR_SUMMARY -->

Changed files

  • lib/crewai/src/crewai/llms/providers/bedrock/completion.py (modified, +24/-12)

PR #4772: fix: group consecutive tool results into single Bedrock user message

Description (problem / solution / changelog)

Summary

Fixes #4749

When a Bedrock model makes multiple tool calls in one assistant response, CrewAI sends each tool result as a separate user message. Bedrock's Converse API requires all toolResult blocks for a given assistant turn to be grouped in a single user message, causing a ValidationException.

Problem

Before (broken) — each tool result is a separate message:

Message 0: user     → "Calculate 25+17 AND 10*5"
Message 1: assistant → [toolUse: add_tool, toolUse: multiply_tool]
Message 2: user     → [toolResult: add_tool]       ← separate message
Message 3: user     → [toolResult: multiply_tool]   ← separate message — ERROR

Bedrock rejects this with:

ValidationException: Expected toolResult blocks at messages.2.content
for the following Ids: tooluse_xxx, tooluse_yyy

After (fixed) — consecutive tool results grouped into one message:

Message 0: user     → "Calculate 25+17 AND 10*5"
Message 1: assistant → [toolUse: add_tool, toolUse: multiply_tool]
Message 2: user     → [toolResult: add_tool, toolResult: multiply_tool]  ← single message

Changes

lib/crewai/src/crewai/llms/providers/bedrock/completion.py

In _format_messages_for_converse(), the role == "tool" handler previously called converse_messages.append(...) for every tool result. Now it checks whether the previous message is already a user message containing toolResult blocks, and if so, appends the new toolResult block to that existing message instead of creating a new one.

lib/crewai/tests/llms/bedrock/test_bedrock.py

Added three test cases:

  • test_bedrock_groups_consecutive_tool_results — two parallel tool calls produce one user message with two toolResult blocks
  • test_bedrock_single_tool_result_still_works — single tool call still works correctly (regression guard)
  • test_bedrock_groups_three_tool_results — three parallel tool calls produce one user message with three toolResult blocks

Test plan

  • New unit tests pass for 1, 2, and 3 parallel tool results
  • Single tool call behavior unchanged
  • No changes to non-tool message handling

🤖 Generated with Claude Code

<!-- CURSOR_SUMMARY -->

[!NOTE] Medium Risk Changes Bedrock request formatting for tool-calling conversations, which could affect downstream tool execution/continuation if edge cases exist, but coverage is added via new regression tests for 1/2/3 tool results.

Overview Fixes Bedrock Converse formatting when an assistant issues multiple tool calls by grouping consecutive tool messages into one user message that contains multiple toolResult blocks (instead of emitting one user message per tool result).

Adds Bedrock unit tests to lock in the expected grouping behavior for single, two, and three consecutive tool results (regression for #4749).

<sup>Written by Cursor Bugbot for commit 2a3ee8c2fec0561cb73cdc98cb54dffb27ff7528. This will update automatically on new commits. Configure here.</sup>

<!-- /CURSOR_SUMMARY -->

Changed files

  • lib/crewai/src/crewai/llms/providers/bedrock/completion.py (modified, +26/-12)
  • lib/crewai/tests/llms/bedrock/test_bedrock.py (modified, +144/-0)

PR #4775: fix(bedrock): group parallel tool results in single user message

Description (problem / solution / changelog)

What does this PR do?

Fixes parallel tool call results not being grouped in a single user message for AWS Bedrock's Converse API.

Problem

When an assistant message contains multiple parallel tool calls (toolUse blocks), Bedrock requires all corresponding toolResult blocks to be sent back in a single user message. Previously, each tool result was emitted as a separate user message:

Message 0: user (prompt)
Message 1: assistant (toolUse A, toolUse B)
Message 2: user (toolResult A)  ← Separate message
Message 3: user (toolResult B)  ← Separate message → ValidationException

This caused: ValidationException: Expected toolResult blocks at messages.2.content

Fix

When processing consecutive role == "tool" messages in _format_messages_for_converse(), the fix checks if the previous converse message is already a user message containing toolResult blocks. If so, it appends the new toolResult to that existing message instead of creating a new one:

Message 0: user (prompt)
Message 1: assistant (toolUse A, toolUse B)
Message 2: user (toolResult A, toolResult B)  ← Grouped in single message ✅

Tests Added

3 regression tests:

  • test_bedrock_parallel_tool_results_grouped — 2 parallel tool calls → single grouped user message
  • test_bedrock_single_tool_result_still_works — single tool call still works correctly
  • test_bedrock_tool_results_not_merged_across_assistant_messages — tool results from different assistant turns remain separate

All 28 existing bedrock tests pass (1 pre-existing failure in test_bedrock_raises_error_when_model_not_found, unrelated).

Fixes #4749

<!-- CURSOR_SUMMARY -->

[!NOTE] Medium Risk Changes core Bedrock tool-calling message serialization, which can affect downstream Converse API validation and tool execution flows, though coverage is improved with targeted regression tests.

Overview Fixes AWS Bedrock Converse message formatting so consecutive role: "tool" results are buffered and emitted as a single role: "user" message containing multiple toolResult blocks, matching Bedrock’s requirement for parallel tool calls.

Adds regression tests covering grouped multi-tool results, ensuring single-tool behavior remains correct, and verifying tool results are not merged across separate assistant turns.

<sup>Written by Cursor Bugbot for commit 61f61c6a5e23dfb0a52876acf2b949d72c4bcaa0. This will update automatically on new commits. Configure here.</sup>

<!-- /CURSOR_SUMMARY -->

Changed files

  • lib/crewai/src/crewai/llms/providers/bedrock/completion.py (modified, +42/-38)
  • lib/crewai/tests/llms/bedrock/test_bedrock.py (modified, +208/-0)

Code Example

from crewai import Agent, Task, Crew
from crewai.tools import tool

@tool("add_tool")
def add_tool(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b

@tool("multiply_tool")
def multiply_tool(a: int, b: int) -> int:
    """Multiply two numbers."""
    return a * b

agent = Agent(
    role="Calculator",
    goal="Perform calculations",
    backstory="Math expert",
    tools=[add_tool, multiply_tool],
    llm="bedrock/us.amazon.nova-pro-v1:0"
)

task = Task(
    description="Calculate 25 + 17 AND 10 * 5",
    expected_output="Both results",
    agent=agent
)

crew = Crew(agents=[agent], tasks=[task])
result = crew.kickoff()

---

Message 0: user (prompt)
Message 1: assistant (toolUse A, toolUse B)
Message 2: user (toolResult A, toolResult B)Both in one message

---

Message 0: user (prompt)
Message 1: assistant (toolUse A, toolUse B)
Message 2: user (toolResult A)Separate message
Message 3: user (toolResult B)Separate message - causes error

---

ValidationException: Expected toolResult blocks at messages.2.content for the following Ids: tooluse_xxx, tooluse_yyy

---

elif role == "tool":
    # Each tool result creates a separate message
    converse_messages.append({
        "role": "user",
        "content": [{
            "toolResult": {
                "toolUseId": tool_call_id,
                "content": [{"text": str(content)}]
            }
        }]
    })

---

# Buffer tool results and flush as single message
pending_tool_results: list[dict] = []

def flush_tool_results() -> None:
    if pending_tool_results:
        converse_messages.append({
            "role": "user",
            "content": pending_tool_results.copy()
        })
        pending_tool_results.clear()

# In message loop:
elif role == "tool":
    pending_tool_results.append({
        "toolResult": {
            "toolUseId": tool_call_id,
            "content": [{"text": str(content)}]
        }
    })
elif role == "assistant":
    flush_tool_results()  # Flush before assistant message
    # ... process assistant message

# Flush at end:
flush_tool_results()

---

ValidationException: Expected toolResult blocks at messages.2.content for the following Ids: tooluse_DR_3f80OR0aRzl1SfLr3yw
RAW_BUFFERClick to expand / collapse

Description

When an AWS Bedrock model makes multiple tool calls in a single response, CrewAI sends each tool result as a separate user message. Bedrock's Converse API requires all tool results for a given assistant message to be grouped together in a single user message, causing a ValidationException.

The bug is in lib/crewai/src/crewai/llms/providers/bedrock/completion.py (~line 1800) where tool messages are converted one at a time without buffering consecutive tool results.

Steps to Reproduce

  1. Configure CrewAI to use an AWS Bedrock model
  2. Create an agent with multiple tools:
from crewai import Agent, Task, Crew
from crewai.tools import tool

@tool("add_tool")
def add_tool(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b

@tool("multiply_tool")
def multiply_tool(a: int, b: int) -> int:
    """Multiply two numbers."""
    return a * b

agent = Agent(
    role="Calculator",
    goal="Perform calculations",
    backstory="Math expert",
    tools=[add_tool, multiply_tool],
    llm="bedrock/us.amazon.nova-pro-v1:0"
)

task = Task(
    description="Calculate 25 + 17 AND 10 * 5",
    expected_output="Both results",
    agent=agent
)

crew = Crew(agents=[agent], tasks=[task])
result = crew.kickoff()
  1. The model attempts to call both tools simultaneously
  2. Bedrock returns ValidationException
  3. Agent fails or retries with single tool calls

Expected behavior

When the model makes multiple tool calls in one response, all tool results should be sent back to Bedrock in a single user message with multiple toolResult content blocks.

Expected message structure:

Message 0: user (prompt)
Message 1: assistant (toolUse A, toolUse B)
Message 2: user (toolResult A, toolResult B)  ← Both in one message

Screenshots/Code snippets

Current broken behavior:

Message 0: user (prompt)
Message 1: assistant (toolUse A, toolUse B)
Message 2: user (toolResult A)  ← Separate message
Message 3: user (toolResult B)  ← Separate message - causes error

Bedrock error:

ValidationException: Expected toolResult blocks at messages.2.content for the following Ids: tooluse_xxx, tooluse_yyy

Current code (bedrock/completion.py ~line 1800):

elif role == "tool":
    # Each tool result creates a separate message
    converse_messages.append({
        "role": "user",
        "content": [{
            "toolResult": {
                "toolUseId": tool_call_id,
                "content": [{"text": str(content)}]
            }
        }]
    })

Proposed fix:

# Buffer tool results and flush as single message
pending_tool_results: list[dict] = []

def flush_tool_results() -> None:
    if pending_tool_results:
        converse_messages.append({
            "role": "user",
            "content": pending_tool_results.copy()
        })
        pending_tool_results.clear()

# In message loop:
elif role == "tool":
    pending_tool_results.append({
        "toolResult": {
            "toolUseId": tool_call_id,
            "content": [{"text": str(content)}]
        }
    })
elif role == "assistant":
    flush_tool_results()  # Flush before assistant message
    # ... process assistant message

# Flush at end:
flush_tool_results()

Operating System

Other (specify in additional context)

Python Version

3.11

crewAI Version

1.10.1 - commit 87759cd from main branch

crewAI Tools Version

N/A

Virtual Environment

Venv

Evidence

AWS Bedrock Documentation:

Community Reports:

Test Results:

  • Single tool call: ✅ Works
  • Multiple tool calls (parallel): ❌ ValidationException
  • Affects all Bedrock models: Nova Pro, Nova Lite, Claude Opus 4.5, Claude Sonnet 3.5

Error message from Bedrock:

ValidationException: Expected toolResult blocks at messages.2.content for the following Ids: tooluse_DR_3f80OR0aRzl1SfLr3yw

Possible Solution

Implement tool result buffering in bedrock/completion.py:

  1. Create a buffer for consecutive tool result messages
  2. When processing tool messages, add them to the buffer instead of immediately appending
  3. Flush the buffer as a single user message when encountering a non-tool message or end of messages
  4. This groups all tool results from parallel calls into one user message

See proposed fix in "Screenshots/Code snippets" section above.

Additional context

OS: MacOS Tahoe 26.3.1

This bug only affects AWS Bedrock models. OpenAI and Ollama models work correctly because they don't have the same message structure requirements.

The bug prevents capable models (like Claude Opus 4.5) from using their parallel tool calling abilities, forcing them to make sequential tool calls which is slower and less efficient.

extent analysis

Fix Plan

To fix the issue, implement tool result buffering in bedrock/completion.py:

  • Create a buffer for consecutive tool result messages
  • When processing tool messages, add them to the buffer instead of immediately appending
  • Flush the buffer as a single user message when encountering a non-tool message or end of messages

Here's the modified code:

pending_tool_results: list[dict] = []

def flush_tool_results() -> None:
    if pending_tool_results:
        converse_messages.append({
            "role": "user",
            "content": pending_tool_results.copy()
        })
        pending_tool_results.clear()

# In message loop:
elif role == "tool":
    pending_tool_results.append({
        "toolResult": {
            "toolUseId": tool_call_id,
            "content": [{"text": str(content)}]
        }
    })
elif role == "assistant":
    flush_tool_results()  # Flush before assistant message
    # ... process assistant message

# Flush at end:
flush_tool_results()

Verification

To verify the fix:

  1. Run the test case with multiple tool calls in parallel.
  2. Check the message structure sent to Bedrock.
  3. Verify that all tool results are grouped together in a single user message.

Extra Tips

  • Make sure to clear the pending_tool_results buffer after flushing to avoid duplicate messages.
  • Consider adding logging to track the buffering and flushing of tool results for debugging purposes.
  • Review the Bedrock documentation and community reports to ensure the fix aligns with the expected message structure and tool result handling.

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

When the model makes multiple tool calls in one response, all tool results should be sent back to Bedrock in a single user message with multiple toolResult content blocks.

Expected message structure:

Message 0: user (prompt)
Message 1: assistant (toolUse A, toolUse B)
Message 2: user (toolResult A, toolResult B)  ← Both in one message

Still need to ship something?

×6

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

Back to top recommendations

TRENDING