Skip to content

Bedrock Agent Error:The number of toolResult blocks exceeds the number of toolUse blocks of previous turn #2327

@ChuckJonas

Description

@ChuckJonas

Initial Checks

Description

I'm running into an issue when running a Bedrock agent in AG-UI mode with tool calls. It appears that the messages are getting reordered on subsequent requests.

Scenerio:

  1. I make a request which invokes multiple tool calls and it succeeds.

  2. The messages sent to bedrock might look something like this:

[
    {
      "role": "user",
      "content": [
        {
          "text": "whats the latest transaction on each of my account"
        }
      ]
    },
    {
      "role": "assistant",
      "content": [
        {
          "text": "I'll help you get the latest transaction for each of your accounts. Let me first retrieve your account information and then get the transaction details for each account."
        }
      ]
    },
    {
      "role": "assistant",
      "content": [
        {
          "toolUse": {
            "toolUseId": "tooluse_zRneXn8WRDuizIChq5lt8A",
            "name": "get-client-wealth-overview",
            "input": {}
          }
        }
      ]
    },
    {
      "role": "user",
      "content": [
        {
          "toolResult": {
            "toolUseId": "tooluse_zRneXn8WRDuizIChq5lt8A",
            "content": [
              {
                "text": "{\"success\":true,\"tool\":\"get-client-wealth-overview\",\"data\":{omitted},\"parameters\":{}}"
              }
            ],
            "status": "success"
          }
        }
      ]
    }
]
  1. I made another request (don't matter what it is)

  2. The messages sent to bedrock are in a different order and now look like this:

[
    {
      "role": "user",
      "content": [
        {
          "text": "whats the latest transaction on each of my account"
        }
      ]
    },
    {
      "role": "assistant",
      "content": [
        {
          "toolUse": {
            "toolUseId": "tooluse_zRneXn8WRDuizIChq5lt8A",
            "name": "get-client-wealth-overview",
            "input": {}
          }
        }
      ]
    },
    {
      "role": "assistant",
      "content": [
        {
          "text": "I'll help you get the latest transaction for each of your accounts. Let me first retrieve your account information and then get the transaction details for each account."
        }
      ]
    },
    {
      "role": "user",
      "content": [
        {
          "toolResult": {
            "toolUseId": "tooluse_zRneXn8WRDuizIChq5lt8A",
            "content": [
              {
                "text": "{\"success\":true,\"tool\":\"get-client-wealth-overview\",\"data\":{omitted},\"parameters\":{}}"
              }
            ],
            "status": "success"
          }
        }
      ]
    }
]

The order of the messages is changed so the assistant response is between the tool_call and tool_result, which is apparently not allowed with anthropic models...

This results in an error when models/bedrock.py sends the request to AWS:

botocore.errorfactory.ValidationException: An error occurred (ValidationException) when calling the ConverseStream operation: The number of toolResult blocks at messages.3.content exceeds the number of toolUse blocks of previous turn.

I'm not totally clear if this is a problem with the AG-UI client application mixing up the messages or if it something in the pydantic agent runtime. Maybe it's something that could be handled by the agent though since it feels like an implementation detail of the specific model that other providers probably don't have.

Stack Trace:

ERROR:    Exception in ASGI application
 Traceback (most recent call last):
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/uvicorn/protocols/http/h11_impl.py", line 403, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/uvicorn/middleware/proxy_headers.py", line 60, in __call__
    return await self.app(scope, receive, send)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/applications.py", line 113, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/middleware/errors.py", line 186, in __call__
    raise exc
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/middleware/errors.py", line 164, in __call__
    await self.app(scope, receive, _send)
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/middleware/base.py", line 189, in __call__
    raise app_exc
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/middleware/base.py", line 144, in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 63, in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
    raise exc
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app
    await app(scope, receive, sender)
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/routing.py", line 716, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/routing.py", line 736, in app
    await route.handle(scope, receive, send)
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/routing.py", line 290, in handle
    await self.app(scope, receive, send)
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/routing.py", line 78, in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
    raise exc
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app
    await app(scope, receive, sender)
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/routing.py", line 76, in app
    await response(scope, receive, send)
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/responses.py", line 270, in __call__
    with collapse_excgroups():
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/contextlib.py", line 155, in __exit__
    self.gen.throw(typ, value, traceback)
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/_utils.py", line 83, in collapse_excgroups
    raise exc
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/responses.py", line 274, in wrap
    await func()
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/starlette/responses.py", line 254, in stream_response
    async for chunk in self.body_iterator:
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/pydantic_ai/ag_ui.py", line 325, in run
    raise e
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/pydantic_ai/ag_ui.py", line 315, in run
    async for event in self._agent_stream(run):
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/pydantic_ai/ag_ui.py", line 349, in _agent_stream
    async with node.stream(run.ctx) as request_stream:
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/contextlib.py", line 204, in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/pydantic_ai/_agent_graph.py", line 306, in stream
    async with self._stream(ctx) as streamed_response:
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/contextlib.py", line 204, in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/pydantic_ai/_agent_graph.py", line 333, in _stream
    async with ctx.deps.model.request_stream(
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/contextlib.py", line 204, in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/pydantic_ai/models/instrumented.py", line 229, in request_stream
    async with super().request_stream(
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/contextlib.py", line 204, in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/pydantic_ai/models/wrapper.py", line 39, in request_stream
    async with self.wrapped.request_stream(messages, model_settings, model_request_parameters) as response_stream:
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/contextlib.py", line 204, in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/pydantic_ai/models/bedrock.py", line 269, in request_stream
    response = await self._messages_create(messages, True, settings, model_request_parameters)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/pydantic_ai/models/bedrock.py", line 360, in _messages_create
    model_response = await anyio.to_thread.run_sync(functools.partial(self.client.converse_stream, **params))
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/anyio/to_thread.py", line 56, in run_sync
    return await get_async_backend().run_sync_in_worker_thread(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2470, in run_sync_in_worker_thread
    return await future
           ^^^^^^^^^^^^
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 967, in run
    result = context.run(func, *args)
             ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/botocore/client.py", line 595, in _api_call
    return self._make_api_call(operation_name, kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/botocore/context.py", line 123, in wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jonas/.pyenv/versions/3.11.4/lib/python3.11/site-packages/botocore/client.py", line 1058, in _make_api_call
    raise error_class(parsed_response, operation_name)
botocore.errorfactory.ValidationException: An error occurred (ValidationException) when calling the ConverseStream operation: The number of toolResult blocks at messages.3.content exceeds the number of toolUse blocks of previous turn.

Example Code

model = BedrockConverseModel(model_name="us.anthropic.claude-sonnet-4-20250514-v1:0")

# Configure MCP server with custom tool call processing and improved connection settings
eg_mcp = MCPServerStreamableHTTP("http://localhost:8080/mcp")
agent = Agent(
    model=model,
    instructions="You are a financial advisor. You are given a user's financial situation and you need to help them make decisions about their money.",
    toolsets=[eg_mcp],
)

# Create the ag_ui app with middleware
app = agent.to_ag_ui()

Python, Pydantic AI & LLM client version

Python: 3.11
Pydantic: 0.4.7
Model: bedrock:us.anthropic.claude-sonnet-4-20250514-v1:0

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions