Checks
Strands Version
1.35.0
Python Version
3.13 (Debian) & 3.14.2 (macOS)
Operating System
Debian (python:3.13-slim via ECS Fargate) & macOS 26
Installation Method
pip
Steps to Reproduce
When a tool returns no content (e.g., an MCP tool returns an empty result), the SDK passes toolResult.content: [] (empty array) to the Bedrock Converse API. This causes a ValidationException with Nemotron models.
Minimal reproducer using the Strands SDK with a tool that returns empty content:
from strands import Agent, tool
from strands.models import BedrockModel
@tool
def list_tables() -> dict:
"""List all tables in the database.
Returns:
A dict with status and content containing table names.
"""
# Returns a pre-formatted tool result with empty content array.
# This is the same format that MCP tools produce when they return
# a successful result with no content items (e.g., postgres-mcp-server
# run_query returning an empty result set).
#
# The SDK's _wrap_tool_result passes dicts with "status" and "content"
# keys through directly without normalization.
return {
"status": "success",
"content": [],
}
# FAILS with Nemotron:
agent = Agent(
model=BedrockModel(model_id="nvidia.nemotron-super-3-120b", streaming=False),
tools=[list_tables],
)
result = agent("What tables are in the database?")
# SUCCEEDS with Claude (same tool, same code):
agent = Agent(
model=BedrockModel(model_id="us.anthropic.claude-sonnet-4-5-20250929-v1:0", streaming=False),
tools=[list_tables],
)
result = agent("What tables are in the database?")
Output with Nemotron (strands-agents 1.35.0):
Tool #1: list_tables
cycle failed
...
botocore.errorfactory.ValidationException: An error occurred (ValidationException) when calling the
Converse operation: The model returned the following errors:
{"error":{"code":"validation_error","message":"Failed to deserialize the JSON body into the target type:
?[2]: Invalid 'messages': missing field `content` at line 1 column 308","param":null,
"type":"invalid_request_error"}}
└ Bedrock region: us-east-1
└ Model id: nvidia.nemotron-super-3-120b
Output with Claude Sonnet 4.5 (same code, same tool):
Tool #1: list_tables
SUCCESS: Agent responded without error.
In a real Strands agent scenario, this happens when:
- Agent calls an MCP tool (e.g.,
postgres-mcp-server run_query)
- The MCP tool returns a successful result but with no content items
_handle_tool_result in mcp_client.py maps the empty content to content: []
- The event loop constructs a
toolResult message with content: []
_format_bedrock_messages passes it through to the Converse API unchanged
- Nemotron's endpoint rejects it; Claude accepts it
Expected Behavior
The SDK should normalize empty toolResult.content arrays before sending to the Converse API, ensuring cross-model compatibility. A toolResult with content: [] should be replaced with content: [{"text": ""}].
Actual Behavior
Nemotron returns a ValidationException:
botocore.errorfactory.ValidationException: An error occurred (ValidationException) when calling the
Converse operation: The model returned the following errors:
{"error":{"code":"validation_error","message":"Failed to deserialize the JSON body into the target type:
?[2]: Invalid 'messages': missing field `content` at line 1 column 308","param":null,
"type":"invalid_request_error"}}
└ Bedrock region: us-east-1
└ Model id: nvidia.nemotron-super-3-120b
Key observations from the error:
- The error format
{"error":{"code":"validation_error","type":"invalid_request_error"}} is OpenAI-style, originating from Nemotron's inference endpoint — not from Bedrock's own validation layer.
- Bedrock accepts the request (no client-side validation error), but the Converse-to-Nemotron translation layer produces a message missing the required
content field when it encounters an empty content array.
- Claude Sonnet 4.5 processes the identical payload successfully.
- The AWS ToolResultBlock docs mark
content as Required: Yes but do not specify a minimum array length, so [] is technically valid per the spec.
Additional Context
This was discovered with a Strands agent using nvidia.nemotron-super-3-120b via the Bedrock Converse API. The agent used the awslabs/postgres-mcp-server MCP tool, which returned empty content for a successful run_query call. The error was 100% reproducible — every tool use cycle that produced an empty tool result triggered the failure.
Possible Solution
Normalize empty toolResult.content arrays in _format_bedrock_messages in src/strands/models/bedrock.py. This follows existing normalization patterns in the same function (filtering SDK_UNKNOWN_MEMBER, dropping DeepSeek reasoningContent) and matches the TypeScript SDK's approach in bedrock.ts → _formatMessages.
# In _format_bedrock_messages, before formatting content blocks:
if "toolResult" in content_block:
if not content_block["toolResult"].get("content"):
content_block["toolResult"]["content"] = [{"text": ""}]
This is:
- Model-agnostic and defensive — protects against all strict model providers, not just Nemotron
- Consistent with existing SDK normalization patterns in
_format_bedrock_messages
- Consistent with the TypeScript SDK's
_formatMessages which already filters empty content
- A single-point fix at the model-provider boundary
Related Issues
Checks
Strands Version
1.35.0
Python Version
3.13 (Debian) & 3.14.2 (macOS)
Operating System
Debian (python:3.13-slim via ECS Fargate) & macOS 26
Installation Method
pip
Steps to Reproduce
When a tool returns no content (e.g., an MCP tool returns an empty result), the SDK passes
toolResult.content: [](empty array) to the Bedrock Converse API. This causes aValidationExceptionwith Nemotron models.Minimal reproducer using the Strands SDK with a tool that returns empty content:
Output with Nemotron (strands-agents 1.35.0):
Output with Claude Sonnet 4.5 (same code, same tool):
In a real Strands agent scenario, this happens when:
postgres-mcp-serverrun_query)_handle_tool_resultinmcp_client.pymaps the empty content tocontent: []toolResultmessage withcontent: []_format_bedrock_messagespasses it through to the Converse API unchangedExpected Behavior
The SDK should normalize empty
toolResult.contentarrays before sending to the Converse API, ensuring cross-model compatibility. AtoolResultwithcontent: []should be replaced withcontent: [{"text": ""}].Actual Behavior
Nemotron returns a
ValidationException:Key observations from the error:
{"error":{"code":"validation_error","type":"invalid_request_error"}}is OpenAI-style, originating from Nemotron's inference endpoint — not from Bedrock's own validation layer.contentfield when it encounters an empty content array.contentasRequired: Yesbut do not specify a minimum array length, so[]is technically valid per the spec.Additional Context
This was discovered with a Strands agent using
nvidia.nemotron-super-3-120bvia the Bedrock Converse API. The agent used theawslabs/postgres-mcp-serverMCP tool, which returned empty content for a successfulrun_querycall. The error was 100% reproducible — every tool use cycle that produced an empty tool result triggered the failure.Possible Solution
Normalize empty
toolResult.contentarrays in_format_bedrock_messagesinsrc/strands/models/bedrock.py. This follows existing normalization patterns in the same function (filteringSDK_UNKNOWN_MEMBER, dropping DeepSeekreasoningContent) and matches the TypeScript SDK's approach inbedrock.ts → _formatMessages.This is:
_format_bedrock_messages_formatMessageswhich already filters empty contentRelated Issues
sdk-typescript/src/models/bedrock.ts → _formatMessages- Python SDK normalization:sdk-python/src/strands/models/bedrock.py → _format_bedrock_messages- Empty content test pattern:sdk-python/tests/strands/experimental/bidi/models/test_nova_sonic.py