Skip to content

fix(agents): drop strict tool schemas on Bedrock Claude past the 20-tool cap - #1050

Open
pavel-brousek-at-usu wants to merge 2 commits into
usestrix:mainfrom
pavel-brousek-at-usu:fix/bedrock-strict-tool-limit
Open

fix(agents): drop strict tool schemas on Bedrock Claude past the 20-tool cap#1050
pavel-brousek-at-usu wants to merge 2 commits into
usestrix:mainfrom
pavel-brousek-at-usu:fix/bedrock-strict-tool-limit

Conversation

@pavel-brousek-at-usu

Copy link
Copy Markdown

Summary

AWS Bedrock's Converse API rejects any request carrying more than 20 strict-mode tool schemas:

litellm.BadRequestError: BedrockException - {"message":"The model returned the following errors:
 Too many strict tools (28). The maximum number of strict tools supported is 20. Try reducing the number of tools
 marked as strict."}

_BASE_TOOLS in strix/agents/factory.py alone is already 29 entries (todos, notes, reporting, Caido proxy tools, agent graph, load_skill, web_search, think) before agent-browser, shell, filesystem, or any registered extra tools are added. Every Bedrock Claude scan hits this on startup, independent of skills or extra tools loaded — load_skill is a single registered tool regardless of how many skill .md files exist, so skill content can't be the cause.

Fix

build_strix_agent now checks the final tool count against the cap once it's known which route is configured (is_claude_model + is_bedrock_route, both already in strix/config/models.py), and if it's a Bedrock Claude route past 20 tools, forces strict_json_schema=False on every tool. Bedrock rejects the whole request over the limit, not just the tools past #20, so partial strictness (e.g. keeping the first 20 strict) isn't a valid option — confirmed by how other SDKs that hit this same Bedrock limit resolved it: pydantic-ai, camel-ai#4171, and datarobot-genai#547 all disable strict mode for the whole toolset rather than trying to keep a subset strict.

_disable_strict_schema returns a copy via dataclasses.replace rather than mutating in place — _BASE_TOOLS/_EXTRA_TOOLS entries are shared module-level singletons reused by every build_strix_agent call, so an in-place flip would leak non-strict schemas into a later non-Bedrock build in the same process (root agent on Bedrock, then a Vertex/OpenAI child, for example). Caught this exact regression with a dedicated test while writing the fix.

Related

No existing issue or PR addresses this (searched strict tools, 20 tools, strict_json_schema, and the literal error text across issues and PRs, open and closed) — this looks like a new report.

Loosely related to the two open Bedrock tool_choice/parallel_tool_calls PRs (#649, #668) and #644 in that all three are Bedrock-Claude-route-specific request-shape fixes living in/near strix/agents/factory.py and strix/core/inputs.py, but this is a distinct bug (tool count vs. tool_choice format) with no code overlap.

Testing

  • Added _bedrock_claude_model fixture + 5 tests to tests/test_agent_tool_registration.py:
    • Bedrock Claude past the cap → every FunctionTool has strict_json_schema=False
    • Non-Bedrock model → default strict schemas preserved
    • Bedrock Claude under the cap (with _BASE_TOOLS monkeypatched short) → default strict schemas preserved
    • Anti-leak regression: a Bedrock build followed by a non-Bedrock build in the same process must not cross-contaminate, and the shared _BASE_TOOLS singletons must be untouched afterward
  • Verified the anti-leak test actually catches the leak: temporarily swapped _disable_strict_schema back to the in-place-mutation version and confirmed the test fails; restored the fix and confirmed it passes again
  • uv run pytest tests/ — 890 passed (883 existing + 7 new/modified)
  • ruff check / ruff format --check — clean
  • bandit -r strix/agents/factory.py — no issues

Test plan

  • Bedrock Claude scan with >20 tools no longer 400s on tool registration
  • Non-Bedrock models keep the SDK's default strict-mode tools
  • Bedrock builds don't leak non-strict schemas into later builds in the same process
  • Full test suite passes

…ool cap

AWS Bedrock's Converse API rejects a request with more than 20
strict-mode tool schemas: `ValidationException: Too many strict tools
(28). The maximum number of strict tools supported is 20.` Strix's own
`_BASE_TOOLS` alone is already 29 entries before agent-browser, shell,
filesystem, or any registered extra tools are added, so every Bedrock
Claude scan hits this on startup regardless of what skills or extra
tools are loaded.

Force strict_json_schema=False on every tool once the count exceeds
the cap, only for Bedrock Claude routes (is_claude_model + is_bedrock_
route). Bedrock rejects the whole request over the limit, not just the
tools past usestrix#20, so partial strictness isn't an option — matches how
pydantic-ai, camel-ai, and other SDKs that hit this same Bedrock limit
resolved it.

_disable_strict_schema returns a copy via dataclasses.replace rather
than mutating in place: _BASE_TOOLS/_EXTRA_TOOLS entries are shared
module-level singletons reused by every build_strix_agent call, so an
in-place flip would leak non-strict schemas into later non-Bedrock
builds in the same process.
@greptile-apps

greptile-apps Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR avoids Bedrock Claude's 20-strict-tool limit by copying oversized tool sets with strict JSON schemas disabled, while preserving shared tool singletons. It also adds coverage for route selection, threshold behavior, and cross-build isolation.

  • Adds Bedrock Claude route and tool-count gating in the agent factory.
  • Uses dataclasses.replace to prevent strictness changes from leaking between agent builds.
  • Adds focused registration tests for Bedrock, non-Bedrock, under-cap, and anti-leak behavior.

Confidence Score: 4/5

The effective-model mismatch should be fixed before merging because explicit Bedrock Claude model overrides can still hit the request-rejection path this PR is intended to remove.

The runner can execute agents with an explicit model that differs from configuration, while the new compatibility gate consults only configuration and can therefore apply the wrong tool-schema behavior.

Files Needing Attention: strix/agents/factory.py

Important Files Changed

Filename Overview
strix/agents/factory.py Adds the strict-schema workaround, but bases route detection on settings rather than the runner's effective model override.
tests/test_agent_tool_registration.py Adds strong coverage for threshold and mutation behavior, though it does not exercise an effective model differing from settings.
Prompt To Fix All With AI
### Issue 1
strix/agents/factory.py:641
**Effective model route mismatch**

When `run_strix_scan(model=...)` selects a Bedrock Claude model that differs from `settings.llm.model`, this gate classifies the settings model instead of the effective `RunConfig` model, leaving more than 20 strict tools enabled and causing Bedrock to reject the scan at startup.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "fix(agents): drop strict tool schemas on..." | Re-trigger Greptile

Comment thread strix/agents/factory.py Outdated
Addresses review feedback on usestrix#1050: when run_strix_scan(model=...)
selects a model that differs from settings.llm.model, the strict-tool
gate was classifying the wrong model, silently leaving >20 strict
tools enabled and letting Bedrock reject the scan at startup anyway.

build_strix_agent now takes an optional model_name, threaded through
from runner.py's already-resolved resolved_model (falls back to
settings.llm.model for callers, e.g. tests, that don't go through the
runner). make_child_factory forwards the same value so children built
later in the scan see the same classification as the root agent.
@ctrlaltdylan

Copy link
Copy Markdown

Running into this same issue!

Thanks @pavel-brousek-at-usu

1 similar comment
@WhiteHeal

Copy link
Copy Markdown

Running into this same issue!

Thanks @pavel-brousek-at-usu

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants