2025, Nov 22 15:00
How to fix AWS Bedrock Runtime ParamValidationError when combining text and PDF: use separate messages.content blocks
Resolve AWS Bedrock Runtime ParamValidationError when sending a PDF and prompt. Split text and document into separate messages.content blocks in boto3.
Passing a PDF and a prompt to AWS Bedrock Runtime in a single content block looks harmless, until it breaks with a cryptic validation error. If your call to converse on model amazon.nova-premier-v1:0 fails when you combine text and document, the issue is not the model. It’s the shape of messages.content and how Bedrock enforces a tagged union.
Repro case in code
The following snippet opens a PDF, sends it along with a prompt, and triggers a validation error. The logic is straightforward, but the structure of the payload is not what Bedrock expects.
def run_flow():
with open("file.pdf", "rb") as fh:
pdf_bytes = fh.read()
rt = boto3.client("bedrock-runtime", "us-east-1")
out = rt.converse(
modelId=MODEL_ID,
messages=[
{
"role": "user",
"content": [
{
"text": "explain this document",
"document": {
"format": "pdf",
"name": "file",
"source": {
"bytes": pdf_bytes,
},
},
},
],
},
],
)
print(out)
return outThe service responds with a clear hint:
botocore.exceptions.ParamValidationError: Parameter validation failed: Invalid number of parameters set for tagged union structure messages[0].content[0]. Can only set one of the following keys: text, image, document, video, toolUse, toolResult, guardContent, cachePoint, reasoningContent.
What’s really going on
In Bedrock Runtime, messages[...].content is an array of content blocks, and each block is a union. Only one member of that union can be set within a single block. The official description makes it explicit:
This data type is a UNION, so only one of the following members can be specified when used or returned.
Placing both text and document in the same content element violates the union rule. That’s why the validator rejects the request even though both fields are valid on their own.
The fix: split content into separate blocks
The correct approach is to send the PDF as one content block and the prompt as another content block in the same message. This keeps each block compliant with the union constraint while preserving the intended semantics.
def run_flow():
with open("file.pdf", "rb") as fh:
pdf_bytes = fh.read()
rt = boto3.client("bedrock-runtime", "us-east-1")
out = rt.converse(
modelId=MODEL_ID,
messages=[
{
"role": "user",
"content": [
{
"document": {
"format": "pdf",
"name": "file",
"source": {
"bytes": pdf_bytes,
},
},
},
{
"text": "explain this document",
},
],
},
],
)
print(out)
return outWhy this detail matters
Once you start mixing modalities with Bedrock Runtime, the shape of your payload becomes as important as its content. Misunderstanding the union contract leads to hard stops before the model even runs, and the error surfaces at the client level. Knowing that messages.content is an array of single-typed blocks saves time, keeps requests predictable, and aligns your code with how the API is designed to reason over separate inputs.
Takeaways
If you need to send a prompt and a document together, keep them in distinct content blocks within the same message. Treat each block as one and only one of text, document, image, and so on. This small structural change resolves the validation error and lets the model receive both the file and the instruction exactly as intended.