🔴 Required Information
Describe the Bug:
When using RemoteA2aAgent registered as a sub_agent on an LlmAgent, the parent agent's flow terminates immediately after the A2A sub-agent completes, without returning control to the parent LLM for summarization or further processing. According to the ADK A2A documentation (adk.dev/a2a/intro/), interacting with a remote agent should feel like "interacting with a local tool or function" where "the parent agent maintains full control" and "responses are returned as standard tool/function call return values." However, the actual behavior is that the LLM wrapper's chat-mode loop detects event.actions.transfer_to_agent and sets transferred = True, causing the outer loop to return immediately instead of re-entering the LLM with the sub-agent's result.
Steps to Reproduce:
- Create an
LlmAgent with a RemoteA2aAgent registered via sub_agents=[remote_agent]
- Ensure the parent LLM's system prompt instructs it to use
transfer_to_agent when it needs to delegate to the sub-agent
- Run the parent agent with a query that triggers the
transfer_to_agent call
- Observe that after the A2A sub-agent completes and returns its response, the entire flow ends — the parent LLM never gets another turn to process or summarize the sub-agent's output
Expected Behavior:
After the A2A sub-agent completes, control should return to the parent LLM with the sub-agent's result as a FunctionResponse (similar to how task delegation via AgentTool works). The parent LLM should then see the result and be able to summarize, analyze, or continue the conversation based on the sub-agent's output. This matches the ADK A2A documentation: "Parent LLM sees results as regular Tool Output."
Observed Behavior:
The flow terminates immediately after the A2A sub-agent completes. The _llm_agent_wrapper.py chat-mode loop (run_llm_agent_as_node) detects event.actions.transfer_to_agent and:
- Sets
transferred = True
- Breaks the inner loop
- Outer loop checks
if not had_task_fc or transferred: return
- Returns immediately — the parent LLM never re-enters to process the result
Environment Details:
- ADK Library Version: 2.0 (latest as of 2026-06-05)
- Desktop OS: Windows
- Python Version: 3.13
Model Information:
- Are you using LiteLLM: Yes
- Which model is being used: qwen 3.6 plus (via Model Gateway)
🟡 Optional Information
Regression:
N/A — This appears to be the intended behavior of transfer_to_agent (scheduler-level routing), but it conflicts with the A2A documentation which describes sub-agents as tool-like call-and-return interactions. This is the first version of ADK with A2A support that we have tested.
Logs:
# Source code analysis from _llm_agent_wrapper.py (lines ~415-445):
while True:
had_task_fc = False
transferred = False
run_method = agent.run_live(ic) if is_live else agent.run_async(ic)
async with aclosing(run_method) as run_iter:
async for event in run_iter:
yield event
task_fcs = _extract_task_delegation_fcs(event, tools_dict)
for fc in task_fcs:
output = await _dispatch_task_fc(agent, fc, ctx)
yield _synthesize_task_fr_event(fc, output)
if task_fcs:
had_task_fc = True
break
if event.actions.transfer_to_agent:
target_name = event.actions.transfer_to_agent
if target_name != agent.name:
transferred = True
break
if not had_task_fc or transferred:
# LLM finished without delegating (or transferred away);
# nothing more for this wrapper to do.
return
# Otherwise: loop back to re-enter agent.run_async so the LLM
# sees the synthesized FR(s) and can emit follow-up actions.
Screenshots / Video:
N/A — This is a control flow issue, not a visual/rendering bug.
Additional Context:
The root cause is a semantic mismatch between transfer_to_agent and the documented A2A call-and-return model:
transfer_to_agent is designed as a scheduler routing signal (handoff control to another agent permanently)
- A2A sub-agents are documented as call-and-return (like a tool call, parent resumes after result)
The _dynamic_node_scheduler.py while True loop correctly handles transfer_to_agent by routing to the target agent and continuing. However, _llm_agent_wrapper.py's outer loop interprets transferred = True as "flow is done, return to caller." This works for true handoff scenarios but breaks the A2A use case where the parent should see the result and continue.
For contrast, AgentTool (wrapping a sub-agent as a function tool) correctly returns a FunctionResponse to the LLM, which then autonomously decides next steps — this is the behavior described in the A2A docs.
Minimal Reproduction Code:
from google.adk.agents.llm_agent import LlmAgent
from google.adk.agents.remote_a2a_agent import RemoteA2aAgent
from google.adk.runners import Runner
from google.adk.sessions.in_memory_session_service import InMemorySessionService
# Create a remote A2A agent
remote_agent = RemoteA2aAgent(
name="sub_agent",
agent_card="http://localhost:8080/.well-known/agent-card.json",
description="A test sub-agent",
)
# Create parent LlmAgent with sub_agent
parent_agent = LlmAgent(
name="parent_agent",
model=model,
instruction="You are a helpful assistant. When you need help, transfer to the sub_agent.",
tools=[],
sub_agents=[remote_agent],
)
runner = Runner(
app_name="test",
agent=parent_agent,
session_service=InMemorySessionService(),
)
# Run — after sub_agent completes, flow ends immediately
async for event in runner.run_async(
user_id="test",
session_id="test-session",
new_message=new_message,
):
print(event)
if event.is_final_response():
# This fires after sub_agent completes,
# but parent LLM never got to see the result
break
How often has this issue occurred?:
🔴 Required Information
Describe the Bug:
When using
RemoteA2aAgentregistered as asub_agenton anLlmAgent, the parent agent's flow terminates immediately after the A2A sub-agent completes, without returning control to the parent LLM for summarization or further processing. According to the ADK A2A documentation (adk.dev/a2a/intro/), interacting with a remote agent should feel like "interacting with a local tool or function" where "the parent agent maintains full control" and "responses are returned as standard tool/function call return values." However, the actual behavior is that the LLM wrapper's chat-mode loop detectsevent.actions.transfer_to_agentand setstransferred = True, causing the outer loop toreturnimmediately instead of re-entering the LLM with the sub-agent's result.Steps to Reproduce:
LlmAgentwith aRemoteA2aAgentregistered viasub_agents=[remote_agent]transfer_to_agentwhen it needs to delegate to the sub-agenttransfer_to_agentcallExpected Behavior:
After the A2A sub-agent completes, control should return to the parent LLM with the sub-agent's result as a
FunctionResponse(similar to how task delegation viaAgentToolworks). The parent LLM should then see the result and be able to summarize, analyze, or continue the conversation based on the sub-agent's output. This matches the ADK A2A documentation: "Parent LLM sees results as regular Tool Output."Observed Behavior:
The flow terminates immediately after the A2A sub-agent completes. The
_llm_agent_wrapper.pychat-mode loop (run_llm_agent_as_node) detectsevent.actions.transfer_to_agentand:transferred = Trueif not had_task_fc or transferred: returnEnvironment Details:
Model Information:
🟡 Optional Information
Regression:
N/A — This appears to be the intended behavior of
transfer_to_agent(scheduler-level routing), but it conflicts with the A2A documentation which describes sub-agents as tool-like call-and-return interactions. This is the first version of ADK with A2A support that we have tested.Logs:
Screenshots / Video:
N/A — This is a control flow issue, not a visual/rendering bug.
Additional Context:
The root cause is a semantic mismatch between
transfer_to_agentand the documented A2A call-and-return model:transfer_to_agentis designed as a scheduler routing signal (handoff control to another agent permanently)The
_dynamic_node_scheduler.pywhile Trueloop correctly handlestransfer_to_agentby routing to the target agent and continuing. However,_llm_agent_wrapper.py's outer loop interpretstransferred = Trueas "flow is done, return to caller." This works for true handoff scenarios but breaks the A2A use case where the parent should see the result and continue.For contrast,
AgentTool(wrapping a sub-agent as a function tool) correctly returns aFunctionResponseto the LLM, which then autonomously decides next steps — this is the behavior described in the A2A docs.Minimal Reproduction Code:
How often has this issue occurred?: