Skip to content

SkillToolset causes infinite list_skills/load_skill loop when skill instructions reference a tool not present in the available tool list #6280

Description

@deepakbatham572

When a [SkillToolset] skill's instructions reference a tool (e.g. confirmtool) that is registered via an external/frontend toolset [AGUIToolset] but is not yet present in the agent's available tool list at request time, the agent enters an infinite list_skills → load_skill → list_skills loop and never produces a response.

Root cause (from source analysis):

[DEFAULT_SKILL_SYSTEM_INSTRUCTION] is appended to the agent instruction via [process_llm_request] on every LLM call, telling the model: "you MUST use load_skill before proceeding". There is no guard to skip this injection after the skill has already been activated in the current turn (the adk_activated_skill state key exists but is never used to suppress re-injection).

After load_skill returns, the LLM needs to call a tool referenced in the skill instructions (e.g. confirmtool). If that tool is absent from the tool list — e.g. because it comes from a frontend/HITL toolset that hasn't registered yet — the LLM has no valid next action and falls back to list_skills → load_skill again.

There is no max-iteration guard on this cycle.

To reproduce:

from google.adk.agents import Agent
from google.adk.tools.skill_toolset import SkillToolset, DEFAULT_SKILL_SYSTEM_INSTRUCTION
from google.adk.skills import load_skill_from_dir
from ag_ui_adk import ADKAgent, AGUIToolset

skill-creator/SKILL.md references a tool called "confirmtool"
that comes from AGUIToolset but is NOT yet in RunAgentInput.tools
e.g. on the first request before the frontend finishes mounting)

skill = load_skill_from_dir("skills/skill-test")
skill_toolset = SkillToolset(skills=[skill])

agent = Agent(
    name="test",
    model=model,
    instruction="..." + DEFAULT_SKILL_SYSTEM_INSTRUCTION,
    tools=[AGUIToolset(), skill_toolset],
)

Send a request where [RunAgentInput.tools = []]. Observe infinite loop.

Expected behavior:

One of:

ADK detects that adk_activated_skill is already set and does not re-inject [DEFAULT_SKILL_SYSTEM_INSTRUCTION] on subsequent LLM calls within the same turn
ADK surfaces a clear error when a tool referenced in skill instructions is not available
[SkillToolset] has a max tool-call iteration guard (e.g. 5 cycles) before giving up

Actual behavior:

Infinite loop of list_skills → load_skill → list_skills → load_skill ... until the request times out or is manually killed.

Logs

[ADK_EVENT] author=generate_skill_with_ai content=[FunctionCall: list_skills]
[ADK_EVENT] author=generate_skill_with_ai content=[FunctionCall: load_skill]
[ADK_EVENT] author=generate_skill_with_ai content=[FunctionCall: load_skill_resource] (×4)
[ADK_EVENT] author=generate_skill_with_ai content=[FunctionCall: list_skills]
[ADK_EVENT] author=generate_skill_with_ai content=[FunctionCall: load_skill]
[ADK_EVENT] author=generate_skill_with_ai content=[FunctionCall: list_skills]
... repeats indefinitely

Versions:

Workaround:
Remove [SkillToolset] and [DEFAULT_SKILL_SYSTEM_INSTRUCTION] from the agent when skill instructions reference frontend/HITL tools. Embed the skill instructions directly in the agent's system prompt instead.

Metadata

Metadata

Assignees

No one assigned

    Labels

    tools[Component] This issue is related to tools

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions