-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Open
Description
Initial Checks
- I confirm that I'm using the latest version of Pydantic AI
- I confirm that I searched for my issue in https://github.com/pydantic/pydantic-ai/issues before opening this issue
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 workingSomething isn't working