🔴 Required Information
Describe the Bug:
Any workflow containing a @node(parallel_worker=True) node fed an N-item
list. Every clean, successful run ends with:
WARNING - _workflow.py:854 - Workflow <name>: cancelling N leftover tasks.
where N equals the worker count, even though all workers completed and their outputs
were delivered downstream.
Completed dynamic runs are never removed from DynamicNodeState.runs, and
run.task is never cleared:
_dynamic_node_scheduler.py — _record_result() sets status = COMPLETED but does
not prune the run or clear run.task.
_dynamic_node_scheduler.py:101-103 — get_dynamic_tasks() returns
[run.task for run in self.runs.values() if run.task] with no task.done() filter.
_workflow.py:848-858 — _cleanup_all_tasks() logs the warning whenever that list is
non-empty, before checking whether anything actually needs cancelling. The cancel()
itself is guarded by if not task.done(): and no-ops for finished workers.
Expected Behavior: A clean run should not trigger a warning
Observed Behavior: A false warning being triggered on clean workflow runs
Impact: Cosmetic but misleading. The same warning fires for genuinely stuck tasks,
so on fan-out workflows a real hang becomes indistinguishable from routine noise
Environment Details:
- ADK Library Version (pip show google-adk):
(.venv) ➜ adk git:(main) ✗ pip show google-adk
Name: google-adk
Version: 2.2.0
Summary: Agent Development Kit
Home-page: https://google.github.io/adk-docs/
Author:
Author-email: Google LLC <googleapis-packages@google.com>
License:
Location: /Users/santosflores/Development/agents/adk/.venv/lib/python3.14/site-packages
Requires: aiosqlite, authlib, click, fastapi, google-auth, google-genai, graphviz, httpx, jsonschema, opentelemetry-api, opentelemetry-sdk, packaging, pydantic, python-dotenv, python-multipart, pyyaml, requests, starlette, tenacity, typing-extensions, tzlocal, uvicorn, watchdog, websockets
Required-by:
- Desktop OS: macOS
- Python Version (python -V):
Model Information:
- Are you using LiteLLM: No
- Which model is being used: gemini-3.1-flash-lite
🟡 Optional Information
Providing this information greatly speeds up the resolution process.
Regression:
Did this work in a previous version of ADK? If so, which one?
Logs:
Please attach relevant logs. Wrap them in code blocks (```) or attach a
text file.
INFO: 127.0.0.1:62812 - "POST /run_sse HTTP/1.1" 200 OK
2026-06-11 08:47:28,858 - INFO - google_llm.py:208 - Sending out request, model: gemini-3.1-flash-lite, backend: GoogleLLMVariant.VERTEX_AI, stream: False
2026-06-11 08:47:28,859 - INFO - models.py:8635 - AFC is enabled with max remote calls: 10.
2026-06-11 08:47:30,890 - INFO - google_llm.py:277 - Response received from the model.
/Users/santosflores/Development/agents/adk/.venv/lib/python3.14/site-packages/google/adk/tools/mcp_tool/mcp_toolset.py:314: UserWarning: [EXPERIMENTAL] feature FeatureName._MCP_GRACEFUL_ERROR_HANDLING is enabled.
session = await self._mcp_session_manager.create_session(
2026-06-11 08:47:31,244 - INFO - _client.py:1740 - HTTP Request: POST https://mcp.serpapi.com/9fe5b2435864e377177514a474d1390dbc9dc6ed7ae9d9cfb72e97ffa80eff80/mcp "HTTP/1.1 200 OK"
2026-06-11 08:47:31,247 - INFO - streamable_http.py:193 - Negotiated protocol version: 2025-11-25
2026-06-11 08:47:31,335 - INFO - _client.py:1740 - HTTP Request: POST https://mcp.serpapi.com/9fe5b2435864e377177514a474d1390dbc9dc6ed7ae9d9cfb72e97ffa80eff80/mcp "HTTP/1.1 202 Accepted"
2026-06-11 08:47:31,613 - INFO - _client.py:1740 - HTTP Request: POST https://mcp.serpapi.com/9fe5b2435864e377177514a474d1390dbc9dc6ed7ae9d9cfb72e97ffa80eff80/mcp "HTTP/1.1 200 OK"
/Users/santosflores/Development/agents/adk/.venv/lib/python3.14/site-packages/google/adk/features/_feature_decorator.py:72: UserWarning: [EXPERIMENTAL] feature FeatureName.BASE_AUTHENTICATED_TOOL is enabled.
check_feature_enabled()
2026-06-11 08:47:31,617 - INFO - _client.py:1740 - HTTP Request: POST https://mcp.serpapi.com/9fe5b2435864e377177514a474d1390dbc9dc6ed7ae9d9cfb72e97ffa80eff80/mcp "HTTP/1.1 200 OK"
2026-06-11 08:47:31,618 - INFO - _client.py:1740 - HTTP Request: POST https://mcp.serpapi.com/9fe5b2435864e377177514a474d1390dbc9dc6ed7ae9d9cfb72e97ffa80eff80/mcp "HTTP/1.1 200 OK"
2026-06-11 08:47:31,857 - INFO - _client.py:1740 - HTTP Request: POST https://mcp.serpapi.com/9fe5b2435864e377177514a474d1390dbc9dc6ed7ae9d9cfb72e97ffa80eff80/mcp "HTTP/1.1 200 OK"
2026-06-11 08:47:31,867 - INFO - _client.py:1740 - HTTP Request: POST https://mcp.serpapi.com/9fe5b2435864e377177514a474d1390dbc9dc6ed7ae9d9cfb72e97ffa80eff80/mcp "HTTP/1.1 200 OK"
2026-06-11 08:47:31,895 - INFO - _client.py:1740 - HTTP Request: POST https://mcp.serpapi.com/9fe5b2435864e377177514a474d1390dbc9dc6ed7ae9d9cfb72e97ffa80eff80/mcp "HTTP/1.1 200 OK"
2026-06-11 08:47:32,140 - INFO - _client.py:1740 - HTTP Request: POST https://mcp.serpapi.com/9fe5b2435864e377177514a474d1390dbc9dc6ed7ae9d9cfb72e97ffa80eff80/mcp "HTTP/1.1 200 OK"
2026-06-11 08:47:32,182 - INFO - _client.py:1740 - HTTP Request: POST https://mcp.serpapi.com/9fe5b2435864e377177514a474d1390dbc9dc6ed7ae9d9cfb72e97ffa80eff80/mcp "HTTP/1.1 200 OK"
2026-06-11 08:47:32,247 - INFO - _client.py:1740 - HTTP Request: POST https://mcp.serpapi.com/9fe5b2435864e377177514a474d1390dbc9dc6ed7ae9d9cfb72e97ffa80eff80/mcp "HTTP/1.1 200 OK"
2026-06-11 08:47:32,484 - INFO - _client.py:1740 - HTTP Request: POST https://mcp.serpapi.com/9fe5b2435864e377177514a474d1390dbc9dc6ed7ae9d9cfb72e97ffa80eff80/mcp "HTTP/1.1 200 OK"
2026-06-11 08:47:32,517 - INFO - _client.py:1740 - HTTP Request: POST https://mcp.serpapi.com/9fe5b2435864e377177514a474d1390dbc9dc6ed7ae9d9cfb72e97ffa80eff80/mcp "HTTP/1.1 200 OK"
2026-06-11 08:47:32,813 - INFO - _client.py:1740 - HTTP Request: POST https://mcp.serpapi.com/9fe5b2435864e377177514a474d1390dbc9dc6ed7ae9d9cfb72e97ffa80eff80/mcp "HTTP/1.1 200 OK"
2026-06-11 08:47:32,824 - INFO - agent.py:96 - len(posts) before deduped: 124
2026-06-11 08:47:32,824 - INFO - agent.py:98 - len(posts) after deduped: 99
2026-06-11 08:47:32,827 - WARNING - _workflow.py:854 - Workflow root_agent: cancelling 3 leftover tasks.
Screenshots / Video:
If applicable, add screenshots or screen recordings to help explain
your problem.
Additional Context:
Add any other context about the problem here.
Minimal Reproduction Code:
import asyncio
import logging
from google.adk import Workflow
from google.adk.runners import InMemoryRunner
from google.adk.workflow import node
from google.genai import types
logging.basicConfig(level=logging.WARNING)
@node
def split(node_input):
return [1, 2, 3]
@node(parallel_worker=True)
def work(node_input: int) -> int:
return node_input * 2
@node
def collect(node_input: list):
return node_input
root_agent = Workflow(
name="repro",
edges=[("START", split), (split, work), (work, collect)],
)
async def main():
runner = InMemoryRunner(agent=root_agent)
session = await runner.session_service.create_session(
app_name=runner.app_name, user_id="u"
)
async for event in runner.run_async(
user_id="u",
session_id=session.id,
new_message=types.Content(role="user", parts=[types.Part(text="go")]),
):
pass
# Expected: clean run, workers output [2, 4, 6], no warnings.
# Actual: WARNING ... Workflow repro: cancelling 3 leftover tasks.
asyncio.run(main())
How often has this issue occurred?:
🔴 Required Information
Describe the Bug:
Any workflow containing a
@node(parallel_worker=True)node fed an N-itemlist. Every clean, successful run ends with:
where N equals the worker count, even though all workers completed and their outputs
were delivered downstream.
Completed dynamic runs are never removed from
DynamicNodeState.runs, andrun.taskis never cleared:_dynamic_node_scheduler.py—_record_result()setsstatus = COMPLETEDbut doesnot prune the run or clear
run.task._dynamic_node_scheduler.py:101-103—get_dynamic_tasks()returns[run.task for run in self.runs.values() if run.task]with notask.done()filter._workflow.py:848-858—_cleanup_all_tasks()logs the warning whenever that list isnon-empty, before checking whether anything actually needs cancelling. The
cancel()itself is guarded by
if not task.done():and no-ops for finished workers.Expected Behavior: A clean run should not trigger a warning
Observed Behavior: A false warning being triggered on clean workflow runs
Impact: Cosmetic but misleading. The same warning fires for genuinely stuck tasks,
so on fan-out workflows a real hang becomes indistinguishable from routine noise
Environment Details:
Model Information:
🟡 Optional Information
Providing this information greatly speeds up the resolution process.
Regression:
Did this work in a previous version of ADK? If so, which one?
Logs:
Please attach relevant logs. Wrap them in code blocks (```) or attach a
text file.
Screenshots / Video:
If applicable, add screenshots or screen recordings to help explain
your problem.
Additional Context:
Add any other context about the problem here.
Minimal Reproduction Code:
How often has this issue occurred?: