Skip to content

fix: isolate toolset failures in SkillToolset additional-tools resolution - #6526

Open
mittalpk wants to merge 1 commit into
google:mainfrom
mittalpk:fix/skill-toolset-toolset-failure-isolation
Open

fix: isolate toolset failures in SkillToolset additional-tools resolution#6526
mittalpk wants to merge 1 commit into
google:mainfrom
mittalpk:fix/skill-toolset-toolset-failure-isolation

Conversation

@mittalpk

Copy link
Copy Markdown

Summary

Fixes #6507.

_resolve_additional_tools_from_state in skill_toolset.py used a bare asyncio.gather(...) over every provided toolset's get_tools_with_prefix() call. Since gather() propagates the first exception by default, a single toolset that fails to list its tools (e.g. an McpToolset whose server is unreachable/restarting) aborted resolution of every additional tool for that invocation — including plain BaseTool instances and healthy toolsets with no relationship to the failing one.

This is easy to hit with MCP-backed toolsets in practice: McpToolset.get_tools opens a live session, so any restart/503/network blip makes listing raise, and the blast radius extends to every unrelated adk_additional_tools entry.

Fix

asyncio.gather(..., return_exceptions=True), then per-toolset:

  • skip (with a logger.warning) any toolset whose listing raised, so it contributes no tools for that invocation but doesn't block the others
  • re-raise asyncio.CancelledError so cancellation still propagates correctly

Testing plan

  • Added test_resolve_additional_tools_toolset_failure_does_not_abort_others: a SkillToolset configured with one healthy individual tool, one healthy toolset, and one toolset whose get_tools_with_prefix raises ConnectionError — asserts the healthy tool and healthy toolset's tool both resolve, and the failing toolset is silently skipped.
  • Added test_resolve_additional_tools_reraises_cancelled_error: confirms asyncio.CancelledError from a toolset still propagates out of resolution rather than being swallowed.
  • Confirmed both new tests fail against the pre-fix code (git stash) and pass after the fix.
  • pytest tests/unittests/tools/test_skill_toolset.py: 128 passed.
  • pytest tests/unittests/tools/: 1694 passed, 0 failed (only pre-existing unrelated deprecation warnings).
  • pre-commit run --files src/google/adk/tools/skill_toolset.py tests/unittests/tools/test_skill_toolset.py: all hooks pass (ruff, isort, pyink, addlicense, compliance-checks, codespell).

🤖 Generated with Claude Code

…tion

_resolve_additional_tools_from_state used a bare asyncio.gather() over
every provided toolset's get_tools_with_prefix() call. Since gather()
propagates the first exception by default, one toolset failing to list
its tools (e.g. an McpToolset whose server is unreachable) aborted
resolution of every additional tool for that invocation, including
plain BaseTool instances and healthy toolsets with no relationship to
the failing one.

Pass return_exceptions=True and skip (with a warning log) any toolset
whose listing raised, while still re-raising CancelledError so
cancellation continues to propagate. This confines a toolset failure
to that toolset alone.

Fixes google#6507
@adk-bot adk-bot added the tools [Component] This issue is related to tools label Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

tools [Component] This issue is related to tools

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SkillToolset additional-tools resolution aborts entirely when one provided toolset fails to list its tools

3 participants