Repository navigation
Description
🔴 Required Information
Describe the Bug:
LlmAgent.model_post_init adds a tool for each single_turn or task sub-agent (_SingleTurnAgentTool / _TaskAgentTool), and FinishTaskTool when the agent itself is in task mode. LlmAgent.clone() goes through BaseAgent.clone(), which uses model_copy. model_copy does not run model_post_init again, and tools is only shallow-copied, so the clone keeps the original's tool objects:
- The clone's sub-agent tools still wrap the original's sub-agents, not the clone's. A
single_turncall from the clone runs the original sub-agent, so changes made to the clone's sub-agents are ignored. clone(update={"sub_agents": [...]})does not add tools for the new sub-agents, and keeps the tools for the old ones. The model cannot call the new sub-agents. Calling an old one runs an agent that is no longer in the tree (single_turn), or fails withWorkflowConfigurationError: Task target agent '...' not found.(task).clone(update={"tools": [...]})drops the sub-agent tools, so those sub-agents cannot be called at all.clone(update={"mode": ...})orclone(update={"output_schema": ...})leavesfinish_taskas it was on the original.
Steps to Reproduce:
- Run the code below.
Expected Behavior:
A clone has the same tools it would get if it were constructed with its own fields: one tool per single_turn / task sub-agent of the clone, and finish_task matching the clone's mode and output schema.
True
False
['other'] ['other']
['helper']
Observed Behavior:
False
True
['other'] ['helper']
[]
Environment Details:
- ADK Library Version (pip show google-adk): 2.13.0 (also on
mainat e9e8f61) - Desktop OS: macOS
- Python Version (python -V): 3.14.7
Model Information:
- Are you using LiteLLM: No
- Which model is being used: N/A (no model call is needed to reproduce)
🟡 Optional Information
Additional Context:
Running a clone through InMemoryRunner with a mocked model shows the same thing. When the clone's single_turn sub-agent has a changed instruction, the sub-agent's model still receives the original instruction.
task delegation itself looks up the target with root_agent.find_agent(fc.name) in _dispatch_task_fc, so a plain clone() of a task sub-agent still reaches the clone's sub-agent. The update cases above are where task breaks.
Minimal Reproduction Code:
from google.adk.agents import LlmAgent
helper = LlmAgent(name="helper", description="Helps", mode="single_turn")
root = LlmAgent(name="root", sub_agents=[helper])
clone = root.clone()
print(clone.tools[0].agent is clone.sub_agents[0])
print(clone.tools[0].agent is helper)
other = LlmAgent(name="other", description="Other", mode="single_turn")
clone = root.clone(update={"sub_agents": [other]})
print([a.name for a in clone.sub_agents], [t.name for t in clone.tools])
clone = root.clone(update={"tools": []})
print([t.name for t in clone.tools])How often has this issue occurred?:
- Always (100%)
Activity
llalitkumarrr commentedon Nov 5, 2026
Hello @hyeonsang010716,
Thank you for raising this issue and for your contribution. We were able to reproduce the problem and have tested your PR which should resolve it. However the PR is still under review and can only be merged once the reviewers approve the changes. In the meantime we appreciate your patience. We will reach out if we need further information.
fix: give a cloned LlmAgent tools for its own sub-agents
fix: revert the change about giving a cloned LlmAgent tools for its o…
fix: give a cloned LlmAgent tools for its own sub-agents