Skip to content

ParallelAgent on Python 3.10 can raise RuntimeError: aclose(): asynchronous generator is already running after one sub-agent fails #5297

@talvaknin744

Description

@talvaknin744

On Python 3.10, google.adk.agents.ParallelAgent uses _merge_agent_run_pre_3_11() instead of asyncio.TaskGroup. If one sub-agent async generator raises,
_merge_agent_run_pre_3_11() cancels sibling processing tasks in finally but does not await those tasks before ParallelAgent._run_async_impl() closes each sub-agent
generator.

That can raise a second cleanup exception:

RuntimeError: aclose(): asynchronous generator is already running

This can mask or pollute the original error, for example a normal Pydantic validation failure from a structured-output sub-agent.

Environment

  • Python: 3.10.12
  • google-adk: 1.26.0
  • litellm: 1.82.1
  • OS: Linux
  • Agent shape: SequentialAgent containing a ParallelAgent with multiple LlmAgent children using output_schema and output_key

Minimal repro

import asyncio
from google.adk.agents.parallel_agent import _merge_agent_run_pre_3_11

async def slow_agent():
    try:
        await asyncio.sleep(10)
        yield "slow-event"
    finally:
        await asyncio.sleep(0.1)

async def failing_agent():
    await asyncio.sleep(0.01)
    raise ValueError("simulated sub-agent validation failure")
    yield "unreachable"

async def main():
    agent_runs = [slow_agent(), failing_agent()]

    try:
        async for event in _merge_agent_run_pre_3_11(agent_runs):
            print("event", event)
    except Exception as exc:
        print("merge raised:", type(exc).__name__, exc)

    for i, agen in enumerate(agent_runs):
        try:
            await agen.aclose()
            print("closed", i)
        except Exception as exc:
            print("close raised:", i, type(exc).__name__, exc)

asyncio.run(main())

## Actual output

merge raised: ValueError simulated sub-agent validation failure
close raised: 0 RuntimeError aclose(): asynchronous generator is already running
closed 1

## Expected behavior

ADK should propagate the original sub-agent failure cleanly and close/cancel sibling sub-agent runs without raising aclose(): asynchronous generator is already running.

## Suspected root cause

In _merge_agent_run_pre_3_11(), the finally block cancels processing tasks:

finally:
  for task in tasks:
    task.cancel()

but does not await their cancellation. Then ParallelAgent._run_async_impl() closes the same async generators:

finally:
  for sub_agent_run in agent_runs:
    await sub_agent_run.aclose()

If a task is still inside async for on a sibling generator, aclose() races that still-running generator.

## Suggested fix

After canceling tasks in _merge_agent_run_pre_3_11(), await them before the parent closes sub-agent generators:

finally:
  for task in tasks:
    task.cancel()
  if tasks:
    await asyncio.gather(*tasks, return_exceptions=True)

In a local simulation, this preserved the original ValueError and allowed both async generators to close cleanly.

Metadata

Metadata

Assignees

Labels

core[Component] This issue is related to the core interface and implementationrequest clarification[Status] The maintainer need clarification or more information from the authorstale[Status] Issues which have been marked inactive since there is no user response

Type

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions