Affected Model(s)
Describe the bug
When a model returns more than one toolUse block in a single assistant turn, the message converter (toAwsBedrockMessage) emits one separate user message per tool result instead of combining them all into a single user message. This produces two violations:
-
Missing toolResult ids in messages[2] — Bedrock Converse (for Claude) enforces the Anthropic Messages contract: all toolResult blocks answering a given assistant turn must appear together in the immediately following user message. The second result landing in messages[3] instead triggers a ValidationException (HTTP 400):
Expected toolResult blocks at messages.2.content for the following Ids: tooluse_<id>
-
Two consecutive user messages — independently violates Bedrock Converse's general user/assistant role-alternation requirement, regardless of model provider.
The failure is deterministic and reproduces on every retry since the message history is replayed unchanged.
To Reproduce
- Define a Genkit flow using
genkitx-aws-bedrock with two or more tools and a structured JSON output schema.
- Prompt the model in a way that causes it to call two tools in a single turn (parallel tool calls).
- Both tools resolve successfully; Genkit sends the follow-up turn with both
toolResponses in one role: "tool" message.
- The Converse API throws
ValidationException: Expected toolResult blocks at messages.2.content ....
Root cause
In the case "tool": branch of toAwsBedrockMessage (lib/esm/aws_bedrock_llms.mjs ~L1224, lib/cjs/aws_bedrock_llms.js ~L1277), each tool response part is pushed as its own user message:
toolResponseParts.map((part) => {
const toolresult = {
role: "user",
content: [{ toolResult: { toolUseId: part.toolResponse.ref, ... } }],
};
awsBedrockMsgs.push(toolresult); // one message per part
});
For an assistant turn with 2 toolUse blocks this produces:
messages[1] — assistant with 2 toolUse blocks
messages[2] — user with only the first toolResult
messages[3] — user with the second toolResult
Bedrock requires messages[2] to contain a toolResult for every toolUse id in messages[1]. Because the second id lands in messages[3], validation fails. Two consecutive user messages also violate Converse's strict role alternation.
Expected behavior
All toolResult blocks from a single Genkit role: "tool" message should be combined into one user message:
case "tool": {
const toolResponseParts = msg.toolResponseParts();
if (toolResponseParts.length > 0) {
const content = toolResponseParts.map((part) => ({
toolResult: {
toolUseId: part.toolResponse.ref,
content: [{ json: { result: part.toolResponse.output } }],
},
}));
awsBedrockMsgs.push({ role: "user", content });
}
break;
}
This yields a single user message whose content holds a toolResult for each id, satisfying both the Converse role-alternation rule and the Anthropic Messages contract enforced by Converse for Claude models.
Screenshots
N/A
Plugin(s) version:
genkitx-aws-bedrock 1.19.1
Genkit version:
genkit 1.39.x
Additional context
- Single tool-call turns are unaffected — only one result to convert, so the bug only surfaces once the model emits parallel tool calls.
- Happy to open a PR with the fix above if useful.
References
The error is a Bedrock Converse ValidationException (HTTP 400). AWS documents the correct shape by example but does not state the grouping rule in plain language; the clearest explicit statement is in the Anthropic docs, because Converse proxies the Anthropic Messages contract for Claude models.
-
Bedrock Converse ValidationException — Converse API reference: confirms ValidationException is HTTP 400 and is raised when the input fails to satisfy Converse's constraints.
-
Message.content is an array — Message API reference: confirms content is an array of ContentBlock, meaning multiple toolResult blocks belong together in one message's content array.
-
Correct tool-result shape (by example) — Client-side tool use (Converse): the official Converse example appends results as a single user message with an array of toolResult content blocks.
-
Explicit grouping rule + wrong/correct example — Anthropic: Parallel tool use → Troubleshooting: states the rule plainly and shows the exact wrong pattern the plugin currently produces:
Whichever strategy you use, return one tool_result for each tool_use block, all together in the next user message.
// Wrong: separate user messages (what the plugin currently produces)
[
{"role": "assistant", "content": [tool_use_1, tool_use_2]},
{"role": "user", "content": [tool_result_1]},
{"role": "user", "content": [tool_result_2]}
]
// Correct: one user message with all results
[
{"role": "assistant", "content": [tool_use_1, tool_use_2]},
{"role": "user", "content": [tool_result_1, tool_result_2]}
]
Affected Model(s)
us.anthropic.claude-sonnet-4-5-20250929-v1:0cross-region inference profile)Describe the bug
When a model returns more than one
toolUseblock in a single assistant turn, the message converter (toAwsBedrockMessage) emits one separateusermessage per tool result instead of combining them all into a singleusermessage. This produces two violations:Missing
toolResultids inmessages[2]— Bedrock Converse (for Claude) enforces the Anthropic Messages contract: alltoolResultblocks answering a given assistant turn must appear together in the immediately followingusermessage. The second result landing inmessages[3]instead triggers aValidationException (HTTP 400):Two consecutive
usermessages — independently violates Bedrock Converse's general user/assistant role-alternation requirement, regardless of model provider.The failure is deterministic and reproduces on every retry since the message history is replayed unchanged.
To Reproduce
genkitx-aws-bedrockwith two or more tools and a structured JSON output schema.toolResponses in onerole: "tool"message.ValidationException: Expected toolResult blocks at messages.2.content ....Root cause
In the
case "tool":branch oftoAwsBedrockMessage(lib/esm/aws_bedrock_llms.mjs~L1224,lib/cjs/aws_bedrock_llms.js~L1277), each tool response part is pushed as its ownusermessage:For an assistant turn with 2
toolUseblocks this produces:messages[1]— assistant with 2toolUseblocksmessages[2]— user with only the firsttoolResultmessages[3]— user with the secondtoolResultBedrock requires
messages[2]to contain atoolResultfor everytoolUseid inmessages[1]. Because the second id lands inmessages[3], validation fails. Two consecutiveusermessages also violate Converse's strict role alternation.Expected behavior
All
toolResultblocks from a single Genkitrole: "tool"message should be combined into oneusermessage:This yields a single
usermessage whosecontentholds atoolResultfor each id, satisfying both the Converse role-alternation rule and the Anthropic Messages contract enforced by Converse for Claude models.Screenshots
N/A
Plugin(s) version:
genkitx-aws-bedrock1.19.1Genkit version:
genkit1.39.xAdditional context
References
The error is a Bedrock Converse
ValidationException (HTTP 400). AWS documents the correct shape by example but does not state the grouping rule in plain language; the clearest explicit statement is in the Anthropic docs, because Converse proxies the Anthropic Messages contract for Claude models.Bedrock Converse
ValidationException—ConverseAPI reference: confirmsValidationExceptionis HTTP 400 and is raised when the input fails to satisfy Converse's constraints.Message.contentis an array —MessageAPI reference: confirmscontentis an array ofContentBlock, meaning multipletoolResultblocks belong together in one message's content array.Correct tool-result shape (by example) — Client-side tool use (Converse): the official Converse example appends results as a single
usermessage with an array oftoolResultcontent blocks.Explicit grouping rule + wrong/correct example — Anthropic: Parallel tool use → Troubleshooting: states the rule plainly and shows the exact wrong pattern the plugin currently produces: