Skip to content

Python: BedrockChatClient sends invalid toolChoice: {"none": {}} — Bedrock rejects it #4529

Closed

Description

@RomanMart

Bug Description

BedrockChatClient._prepare_options() maps tool_choice="none" to toolConfig.toolChoice: {"none": {}}, but the AWS Bedrock Converse / ConverseStream API only accepts auto, any, or tool as valid toolChoice keys. This causes a botocore.exceptions.ParamValidationError at runtime.

Steps to Reproduce

  1. Create an agent with tools using BedrockChatClient
  2. Let the agent's function-invocation loop (FunctionInvocationLayer) exhaust its max iterations or hit the error threshold
  3. The framework sets tool_choice="none" for a final "wrap-up" LLM call (to get a plain-text summary)
  4. _prepare_options translates this to {"none": {}} and includes it in toolConfig.toolChoice
  5. Bedrock rejects the request

Error

botocore.exceptions.ParamValidationError: Parameter validation failed:
Unknown parameter in toolConfig.toolChoice: "none", must be one of: auto, any, tool

Root Cause

In agent_framework_bedrock/_chat_client.py (line ~395), the match statement treats "none" the same as "auto":

match tool_mode.get("mode"):
    case "auto" | "none":
        tool_config["toolChoice"] = {tool_mode.get("mode"): {}}

This produces {"none": {}} which is not a valid Bedrock API parameter.

The "none" mode is set by FunctionInvocationLayer in agent_framework/_tools.py in several places (lines ~2183, 2192, 2221, 2268, 2321, 2360) — typically when max iterations are exhausted or error thresholds are reached, forcing a final non-tool call.

Expected Behavior

When tool_choice="none", the Bedrock client should omit toolConfig entirely from the request (or at minimum omit the toolChoice key), since "none" semantically means "don't use tools on this call." This is how the OpenAI client handles it — by stripping tool_choice when there are no tools.

Suggested Fix

if tool_mode := validate_tool_mode(options.get("tool_choice")):
    match tool_mode.get("mode"):
        case "none":
            # Bedrock doesn't support toolChoice "none".
            # Omit toolConfig entirely so the model won't call tools.
            tool_config = None
        case "auto":
            tool_config = tool_config or {}
            tool_config["toolChoice"] = {"auto": {}}
        case "required":
            tool_config = tool_config or {}
            if required_name := tool_mode.get("required_function_name"):
                tool_config["toolChoice"] = {"tool": {"name": required_name}}
            else:
                tool_config["toolChoice"] = {"any": {}}
        case _:
            raise ValueError(f"Unsupported tool mode for Bedrock: {tool_mode.get('mode')}")

Workaround

Subclass BedrockChatClient and override _prepare_options to intercept the "none" mode:

class PatchedBedrockChatClient(BedrockChatClient):
    def _prepare_options(self, messages, options, **kwargs):
        tc = options.get("tool_choice")
        if tc == "none" or (isinstance(tc, dict) and tc.get("mode") == "none"):
            options = {k: v for k, v in options.items() if k not in ("tool_choice", "tools")}
        return super()._prepare_options(messages, options, **kwargs)

Environment

  • agent-framework-bedrock==1.0.0b260304
  • agent-framework==1.0.0rc3
  • Python 3.13
  • AWS region: eu-central-1
  • Model: eu.anthropic.claude-sonnet-4-20250514-v1:0

Activity

  1. added
    pythonUsage: [Issues, PRs], Target: Python
    triageUsage: [Issues], Target: All issues that still need to be triaged
    on Mar 6, 2026
  2. LEDazzio01 commented on Mar 6, 2026

    @LEDazzio01
    Contributor

    I've submitted a fix for this in PR #4535.

    Root cause: _prepare_options had case "auto" | "none" which produced toolConfig.toolChoice: {"none": {}} — an invalid Bedrock API parameter.

    Fix: Split the match branch so "none" sets tool_config = None, omitting toolConfig entirely from the request. This matches the semantic intent ("don't use tools") and is consistent with how the OpenAI client handles it.

    Added 3 unit tests covering none, auto, and required tool_choice modes.

  3. added a commit that references this issue on Mar 12, 2026
    b6a1315
  4. moved this from Community PR to Done in Agent Frameworkon Mar 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

agentsUsage: [Issues, PRs], Target: Single agentpythonUsage: [Issues, PRs], Target: Python

Type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions