Skip to content

Bedrock Tools returning documents fail with ValidationException #3253

@wdavew

Description

@wdavew

Initial Checks

Description

When a tool returns BinaryContent with document types (PDF, etc.), Bedrock throws a ValidationException:

The model returned the following errors: messages.2: `tool_use` ids were found without 
`tool_result` blocks immediately after: tooluse_XXX. Each `tool_use` block must have a 
corresponding `tool_result` block in the next message.

This is in contrast to returning ImageUrl, which seems to work fine.

In my testing, I found that this works after modifying models/bedrock.py to merge multimodal content responses inside the toolResult block instead of spreading it into a subsequent user message.

Before:

[
  {
    "role": "user",
    "content": [
      {
        "toolResult": {
          "toolUseId": "tooluse_XXX",
          "content": [{"text": "See file ABC"}],
          "status": "success"
        }
      }
    ]
  },
  {
    "role": "user",
    "content": [
      {"text": "This is file ABC:"},
      {"document": {"name": "Document 1", "format": "pdf", "source": {"bytes": "..."}}}
    ]
  }
]

After:

{
  "role": "user",
  "content": [
    {
      "toolResult": {
        "toolUseId": "tooluse_XXX",
        "content": [
          {
            "text": "See file ABC"
          },
          {
            "text": "This is file ABC:"
          },
          {
            "document": {
              "name": "Document 1",
              "format": "pdf",
              "source": {
                "bytes": "..."
              }
            }
          }
        ],
        "status": "success"
      }
    }
  ]
}

I did not find any documentation in AWS Bedrock stating that tool call returns must be formatted this way.
However, including the contents inside the toolReturn part seems better aligned to the Bedrock API.

Example Code

import base64
from pydantic_ai import Agent
from pydantic_ai.models.bedrock import BedrockConverseModel
from pydantic_ai.messages import BinaryContent

SIMPLE_PDF = b"%PDF-1.4\n1 0 obj<</Pages 2 0 R/Type/Catalog>>endobj 2 0 obj<</Count 1/Kids[3 0 R]/Type/Pages>>endobj 3 0 obj<</MediaBox[0 0 612 792]/Parent 2 0 R/Type/Page>>endobj\nxref\n0 4\n0000000000 65535 f\n0000000009 00000 n\n0000000058 00000 n\n0000000111 00000 n\ntrailer<</Root 1 0 R/Size 4>>\nstartxref\n174\n%%EOF"
SIMPLE_IMAGE = base64.b64decode("iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=")

model = BedrockConverseModel('us.anthropic.claude-sonnet-4-5-20250929-v1:0')
agent = Agent(model)


@agent.tool_plain
def get_document() -> BinaryContent:
    return BinaryContent(data=SIMPLE_PDF, media_type='application/pdf')

@agent.tool_plain
def get_image() -> BinaryContent:
    return BinaryContent(data=SIMPLE_IMAGE, media_type='image/png')

if __name__ == '__main__':

    # This works
    result = agent.run_sync('Call get_image and tell me what you see')
    print(result.output)

    # This fails
    result = agent.run_sync('Call get_document and tell me what the document contains')
    print(result.output)

Python, Pydantic AI & LLM client version

1.5.0

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions