Skip to content

Python: fix(bedrock): don't send toolChoice when no tools are configured#5172

Merged
eavanvalkenburg merged 3 commits into
microsoft:mainfrom
Bahtya:fix/bedrock-toolconfig
May 5, 2026
Merged

Python: fix(bedrock): don't send toolChoice when no tools are configured#5172
eavanvalkenburg merged 3 commits into
microsoft:mainfrom
Bahtya:fix/bedrock-toolconfig

Conversation

@Bahtya

@Bahtya Bahtya commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

Problem

When using BedrockChatClient with an Agent that has no tools configured, the client still sends toolConfig.toolChoice to the Bedrock API. AWS Bedrock requires toolConfig.tools to be present whenever toolChoice is specified, causing a 400 validation error.

Root Cause

In _prepare_run_options, when tool_choice is set but _prepare_tools returns None (no tools), the code creates an empty dict with tool_config or {} and adds toolChoice to it. This results in sending {"toolChoice": {"auto": {}}} without any tools key.

Fix

Only set toolChoice when tool_config is not None and has a "tools" key. When no tools are configured, toolChoice is silently skipped.

Fixes #5165

BedrockChatClient was sending toolConfig.toolChoice even when no tools
were configured (tools=None). AWS Bedrock requires toolConfig.tools to
be present whenever toolChoice is specified, causing a 400 validation
error.

Only set toolChoice when tool_config has a 'tools' key present.

Fixes #5165

Signed-off-by: bahtya <[email protected]>
@moonbox3 moonbox3 added the python Usage: [Issues, PRs], Target: Python label Apr 8, 2026
@github-actions github-actions Bot changed the title fix(bedrock): don't send toolChoice when no tools are configured Python: fix(bedrock): don't send toolChoice when no tools are configured Apr 8, 2026

@eavanvalkenburg eavanvalkenburg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a test to verify the behavior?

@Bahtya

Bahtya commented Apr 13, 2026

Copy link
Copy Markdown
Contributor Author

@eavanvalkenburg Thanks for the review! I will add a test to verify that toolChoice is not sent when no tools are provided. Will push the update shortly.

- test_prepare_options_tool_choice_auto_without_tools_omits_tool_config
- test_prepare_options_tool_choice_required_without_tools_omits_tool_config

Verifies that toolConfig is omitted when tool_choice is set but no
tools are provided, preventing ParamValidationError from Bedrock.

@chetantoshniwal chetantoshniwal left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Code Review

Reviewers: 4 | Confidence: 94%

✗ Correctness

The core fix in _chat_client.py is correct: it guards toolChoice assignment behind tool_config and "tools" in tool_config, preventing Bedrock ParamValidationError when tool_choice is set but no tools are provided. The old code used tool_config = tool_config or {} which would create an empty dict with only toolChoice and no tools, which Bedrock rejects. The new tests in test_bedrock_client.py properly cover both auto and required modes without tools. However, test_addition.py at the repo root is a stray duplicate test file with no imports — it would fail if discovered by a test runner and should not be committed.

✗ Security Reliability

The core fix in _chat_client.py is a valid reliability improvement: it prevents sending a toolChoice directive to Bedrock when no tools are defined, which would cause a ParamValidationError. The logic change is sound—it guards toolChoice assignment behind a check for tool_config and 'tools' in tool_config. However, a test-only file test_addition.py was added at the repository root that is non-functional (missing imports, no test infrastructure), appears to be an accidental leftover, and should be removed.

✗ Test Coverage

The production code change correctly guards against sending toolChoice without tools to Bedrock. The two new tests in test_bedrock_client.py properly cover the 'auto' and 'required' no-tools scenarios. However, there is a stray test_addition.py file at the repo root that duplicates these tests but is broken (missing imports and helpers), and there is a missing test for the 'required' mode with a specific required_function_name when no tools are provided.

✗ Design Approach

The core fix in _chat_client.py correctly prevents sending a toolChoice to Bedrock when no tools are registered, avoiding ParamValidationError. However, silently swallowing the constraint for the required mode (which semantically means 'the model MUST call a tool') masks a likely caller misconfiguration — a ValueError would be more appropriate there. More critically, test_addition.py was accidentally committed to the repo root: it is a broken duplicate of the tests already added to test_bedrock_client.py, missing all imports and the _make_client helper, and will fail immediately if pytest discovers it.


Automated review by chetantoshniwal's agents

Comment thread test_addition.py Outdated
Comment thread python/packages/bedrock/agent_framework_bedrock/_chat_client.py
…eError for required without tools

1. Remove test_addition.py — stray duplicate of tests already in
   python/packages/bedrock/tests/test_bedrock_client.py, missing all
   necessary imports and would fail with NameError.

2. Change tool_choice='required' handling to raise ValueError when no
   tools are configured instead of silently falling through. Using
   'required' without tools is a logical contradiction — the model
   must invoke a tool but none exist — so surfacing this as a
   ValueError helps callers catch the misconfiguration early.

3. Update the corresponding test to expect ValueError instead of
   silently omitted toolConfig.
@Bahtya

Bahtya commented Apr 17, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed review @chetantoshniwal! I have addressed both points of feedback:

1. Removed stray duplicate test file (test_addition.py)
This file was a duplicate of the tests already present in python/packages/bedrock/tests/test_bedrock_client.py. It was missing all necessary imports (_make_client, Message, Content, Any) and would have caused NameError failures if discovered by any test runner. The file has been removed entirely.

2. tool_choice='required' now raises ValueError when no tools are configured
Instead of silently skipping toolChoice when no tools are present, the required case now explicitly raises:

case "required":
    if not (tool_config and "tools" in tool_config):
        raise ValueError(
            "tool_choice='required' requires at least one tool to be configured, "
            "but no tools were provided."
        )

This surfaces the caller misconfiguration immediately rather than silently degrading behaviour. The corresponding test (test_prepare_options_tool_choice_required_without_tools_raises) has been updated to expect ValueError via pytest.raises.

@Bahtya

Bahtya commented Apr 17, 2026

Copy link
Copy Markdown
Contributor Author

Hi @eavanvalkenburg @chetantoshniwal, thanks for the review! I'll address all feedback: (1) remove the stray test_addition.py, (2) add proper tests for the fix, (3) consider raising ValueError instead of silently ignoring tool_choice. I'll also make sure the CLA is signed. Working on these updates now.

@Bahtya

Bahtya commented Apr 18, 2026

Copy link
Copy Markdown
Contributor Author

@microsoft-github-policy-service agree

@moonbox3

Copy link
Copy Markdown
Contributor

@Bahtya please resolve open comments when they are indeed addressed/fixed. Thanks.

@Bahtya

Bahtya commented Apr 27, 2026

Copy link
Copy Markdown
Contributor Author

Hi @eavanvalkenburg, I've added the requested tests in test_bedrock_client.py that verify both the auto and required tool_choice modes when no tools are configured. The stray test_addition.py has also been removed and the required mode now raises a ValueError as suggested. Could you take another look?

@Bahtya

Bahtya commented Apr 27, 2026

Copy link
Copy Markdown
Contributor Author

@moonbox3 @eavanvalkenburg I've addressed the feedback in the latest commits: removed the stray test_addition.py, added ValueError for tool_choice='required' with no tools, and added proper tests. The CI workflows are currently stuck at action_required and need maintainer approval to run. Could someone approve the workflow runs so we can verify everything passes?

@eavanvalkenburg eavanvalkenburg enabled auto-merge May 5, 2026 07:51
@moonbox3

moonbox3 commented May 5, 2026

Copy link
Copy Markdown
Contributor

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/bedrock/agent_framework_bedrock
   _chat_client.py38110073%298–299, 315–324, 329, 345–351, 354–355, 363, 380, 389, 400, 402, 404, 426–427, 448, 461, 473, 476, 484–485, 488–489, 491–492, 497–499, 501, 511–512, 534, 541, 550–551, 553–554, 556–558, 560, 562–563, 569–571, 574–575, 581–584, 590–600, 603, 622, 627, 669–670, 683, 709, 721, 726, 735, 739, 747–748, 752, 754–761
TOTAL28326329988% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
5653 30 💤 0 ❌ 0 🔥 1m 34s ⏱️

@eavanvalkenburg eavanvalkenburg added this pull request to the merge queue May 5, 2026
Merged via the queue into microsoft:main with commit f3f71f0 May 5, 2026
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: [Bug]: BedrockChatClient sends toolConfig.toolChoice without toolConfig.tools when agent has no tools

4 participants