Describe the Bug:
A mode='task' sub-agent works fine when the App is not resumable, but breaks when resumability_config.is_resumable=True.
Steps to Reproduce:
- Activate an env with google-adk==2.5.0 available. Also make sure you can connect to an LLM (e.g. set env var GOOGLE_API_KEY=your-key-here or GOOGLE_GENAI_USE_VERTEXAI=1)
- Run the below script with
python main.py --resumable
Expected Behavior:
The script (python main.py --resumable) should run without error like if called without options (python main.py)
Observed Behavior:
Stack trace and error printed:
Traceback (most recent call last):
File "/home/nagyand/git/adk-task-resumable-bug/main.py", line 104, in <module>
asyncio.run(main(args.resumable))
File "REDACTED/lib/python3.11/asyncio/runners.py", line 190, in run
return runner.run(main)
^^^^^^^^^^^^^^^^
File "REDACTED/lib/python3.11/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "REDACTED/lib/python3.11/asyncio/base_events.py", line 654, in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
File "/home/nagyand/git/adk-task-resumable-bug/main.py", line 81, in main
async for event in runner.run_async(
File "REDACTED/lib/python3.11/site-packages/google/adk/runners.py", line 1111, in run_async
async for event in agen:
File "REDACTED/lib/python3.11/site-packages/google/adk/runners.py", line 628, in _run_node_async
await self._cleanup_root_task(task, self.agent.name)
File "REDACTED/lib/python3.11/site-packages/google/adk/runners.py", line 906, in _cleanup_root_task
await task
File "REDACTED/lib/python3.11/site-packages/google/adk/runners.py", line 610, in _drive_root_node
raise e.error
File "REDACTED/lib/python3.11/site-packages/google/adk/workflow/_node_runner.py", line 136, in run
await self._execute_node(ctx, node_input)
File "REDACTED/lib/python3.11/site-packages/google/adk/workflow/_node_runner.py", line 274, in _execute_node
await self._run_node_loop(ctx, node_input)
File "REDACTED/lib/python3.11/site-packages/google/adk/workflow/_node_runner.py", line 288, in _run_node_loop
async for event in agen:
File "REDACTED/lib/python3.11/site-packages/google/adk/workflow/_base_node.py", line 166, in run
async for item in agen:
File "REDACTED/lib/python3.11/site-packages/google/adk/agents/llm_agent.py", line 601, in _run_impl
async for event in agen:
File "REDACTED/lib/python3.11/site-packages/google/adk/workflow/_llm_agent_wrapper.py", line 416, in run_llm_agent_as_node
async for event in run_iter:
File "REDACTED/lib/python3.11/site-packages/google/adk/agents/base_agent.py", line 306, in run_async
async for event in agen:
File "REDACTED/lib/python3.11/site-packages/google/adk/agents/llm_agent.py", line 550, in _run_async_impl
async for event in agen:
File "REDACTED/lib/python3.11/site-packages/google/adk/flows/llm_flows/base_llm_flow.py", line 932, in run_async
async for event in agen:
File "REDACTED/lib/python3.11/site-packages/google/adk/flows/llm_flows/base_llm_flow.py", line 994, in _run_one_step_async
async for event in agen:
File "REDACTED/lib/python3.11/site-packages/google/adk/flows/llm_flows/base_llm_flow.py", line 1281, in _postprocess_handle_function_calls_async
if function_response_event := await functions.handle_function_calls_async(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "REDACTED/lib/python3.11/site-packages/google/adk/flows/llm_flows/functions.py", line 413, in handle_function_calls_async
return await handle_function_call_list_async(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "REDACTED/lib/python3.11/site-packages/google/adk/flows/llm_flows/functions.py", line 459, in handle_function_call_list_async
function_response_events = await asyncio.gather(*tasks)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "REDACTED/lib/python3.11/site-packages/google/adk/flows/llm_flows/functions.py", line 561, in _execute_single_function_call_async
raise tool_error
File "REDACTED/lib/python3.11/site-packages/google/adk/flows/llm_flows/functions.py", line 547, in _execute_single_function_call_async
tool = _get_tool(function_call, tools_dict)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "REDACTED/lib/python3.11/site-packages/google/adk/flows/llm_flows/functions.py", line 1135, in _get_tool
ValueError: Tool 'worker' not found.
Available tools: transfer_to_agent, finish_task
Possible causes:
1. LLM hallucinated the function name - review agent instruction clarity
2. Tool not registered - verify agent.tools list
3. Name mismatch - check for typos
Suggested fixes:
- Review agent instruction to ensure tool usage is clear
- Verify tool is included in agent.tools list
- Check for typos in function name
Environment Details:
- ADK Library Version (pip show google-adk): 2.5.0
- Desktop OS:** [e.g., macOS, Linux, Windows] Linux 6.18.33.2-microsoft-standard-WSL2 (Ubuntu 24.04 WSL)
- Python Version (python -V): 3.11.15
Model Information:
- Are you using LiteLLM: No
- Which model is being used: default, probably gemini-3.5-flash, but doesn't matter
🟡 Optional Information
Minimal Reproduction Code:
Please provide a code snippet or a link to a Gist/repo that isolates the issue.
"""MVP repro for: a `mode='task'` sub-agent works fine when the App is NOT
resumable, but breaks when `resumability_config.is_resumable=True`.
Usage:
activate an env with google-adk==2.5.0 available
export GOOGLE_API_KEY=your-key-here
(or GOOGLE_GENAI_USE_VERTEXAI=1 with appropriate Vertex AI credentials.)
# Baseline (should work):
python main.py
# Reproduce the bug (should error, per the bug report):
python main.py --resumable
error:
line 1135, in _get_tool
raise ValueError(error_msg)
ValueError: Tool 'worker' not found.
Available tools: transfer_to_agent, finish_task
Possible causes:
1. LLM hallucinated the function name - review agent instruction clarity
2. Tool not registered - verify agent.tools list
3. Name mismatch - check for typos
Suggested fixes:
- Review agent instruction to ensure tool usage is clear
- Verify tool is included in agent.tools list
- Check for typos in function name
"""
import argparse
import asyncio
import uuid
from google.adk.agents.llm_agent import LlmAgent
from google.adk.apps.app import App
from google.adk.apps._configs import ResumabilityConfig
from google.adk.runners import Runner
from google.adk.sessions.in_memory_session_service import InMemorySessionService
from google.genai import types
def build_app(resumable: bool) -> App:
# Sub-agent running in "task execution mode".
worker = LlmAgent(
name="worker",
mode="task",
instruction=(
"You are a task worker. Solve the user's request, then you MUST "
"call the `finish_task` tool with your final result."
),
)
# Root agent (chat mode, implicit) that delegates to the task sub-agent.
coordinator = LlmAgent(
name="coordinator",
instruction=(
"You are a coordinator. For any user request, delegate the work "
"to the `worker` agent tool, then relay its result back to the "
"user."
),
sub_agents=[worker],
)
return App(
name="task_resumable_repro",
root_agent=coordinator,
resumability_config=ResumabilityConfig(is_resumable=resumable),
)
async def main(resumable: bool) -> None:
app = build_app(resumable)
session_service = InMemorySessionService()
user_id = "repro-user"
session_id = str(uuid.uuid4())
await session_service.create_session(
app_name=app.name,
user_id=user_id,
session_id=session_id,
)
runner = Runner(app=app, session_service=session_service)
print(f"--- Running with resumability_config.is_resumable={resumable} ---")
new_message = types.Content(
role="user",
parts=[types.Part(text="What is 12 * 7? Please give me the answer.")],
)
async for event in runner.run_async(
user_id=user_id,
session_id=session_id,
new_message=new_message,
):
author = getattr(event, "author", "?")
text = None
if event.content and event.content.parts:
text = "".join(p.text or "" for p in event.content.parts if p.text)
print(f"[event] author={author} text={text!r}")
print("--- Done, no error raised ---")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"--resumable",
action="store_true",
help="Enable App.resumability_config.is_resumable (reproduces the bug).",
)
args = parser.parse_args()
asyncio.run(main(args.resumable))
How often has this issue occurred?:
Describe the Bug:
A
mode='task'sub-agent works fine when the App is not resumable, but breaks whenresumability_config.is_resumable=True.Steps to Reproduce:
python main.py --resumableExpected Behavior:
The script (
python main.py --resumable) should run without error like if called without options (python main.py)Observed Behavior:
Stack trace and error printed:
Environment Details:
Model Information:
🟡 Optional Information
Minimal Reproduction Code:
Please provide a code snippet or a link to a Gist/repo that isolates the issue.
How often has this issue occurred?: