-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Description
Describe the bug
Using the Streaming Tools provided in the official ADK documentation ([guide link](https://google.github.io/adk-docs/streaming/streaming-tools/)), attempting to invoke an async generator (i.e., a function using yield
in an async def
) within an agent causes a failure due to copy.deepcopy
trying to pickle an async_generator
. This results in the following error:
TypeError: cannot pickle 'async_generator' object
The crash seems to originate from the session service (in_memory_session_service.py
) when it attempts to deepcopy
a session that includes an unpickleable async_generator
.
To Reproduce
- Follow the steps in the [Streaming Tools example](https://google.github.io/adk-docs/streaming/streaming-tools/) as is — no modifications needed.
- Alternatively, use the raw code of the example with the
LiteLLM
integration instead of Google Gemini/Vertex (same result, no need for Google Cloud). - Start the app using
adk web
. - Trigger an agent tool that includes an async generator.
- The error occurs during session loading or response building.
Expected behavior
You'd expect the system to either handle async generators in session state correctly (e.g., skip pickling them), or document that they're unsupported with appropriate guards or error messages.
Instead, it blows up trying to deepcopy
an unpickleable async generator, killing the session logic completely.
Screenshots
N/A (see full traceback below instead).
Desktop (please complete the following information):
- OS: Windows 11
- Python version (
python -V
): Python 3.13 - ADK version (
pip show google-adk
):google-adk==1.5.0
Model Information
Using a local LiteLLM integration — any local/forwarded model that responds with streaming works. Replacing with real Gemini API is not required to reproduce this.
Additional context
This issue seems to relate to [#586](#586), where someone experienced a similar crash involving deepcopies and unpickleable async content.
The crash is extremely reproducible and baked into the flow, especially for streaming tools. You can't even play with the tutorial code out of the box without tripping over this.
Please either:
- Avoid storing
async_generator
objects inside session models - Filter them out before deepcopy
- Or document a proper workaround for tool authors.
Stacktrace
Traceback (most recent call last):
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\uvicorn\protocols\http\httptools_impl.py", line 409, in run_asgi
result = await app( # type: ignore[func-returns-value]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
self.scope, self.receive, self.send
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\uvicorn\middleware\proxy_headers.py", line 60, in __call__
return await self.app(scope, receive, send)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\fastapi\applications.py", line 1054, in __call__
await super().__call__(scope, receive, send)
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\starlette\applications.py", line 112, in __call__
await self.middleware_stack(scope, receive, send)
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\starlette\middleware\errors.py", line 187, in __call__
raise exc
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\starlette\middleware\errors.py", line 165, in __call__
await self.app(scope, receive, _send)
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\starlette\middleware\exceptions.py", line 62, in __call__
await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app
raise exc
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\starlette\_exception_handler.py", line 42, in wrapped_app
await app(scope, receive, sender)
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\starlette\routing.py", line 714, in __call__
await self.middleware_stack(scope, receive, send)
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\starlette\routing.py", line 734, in app
await route.handle(scope, receive, send)
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\starlette\routing.py", line 288, in handle
await self.app(scope, receive, send)
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\starlette\routing.py", line 76, in app
await wrap_app_handling_exceptions(app, request)(scope, receive, send)
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app
raise exc
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\starlette\_exception_handler.py", line 42, in wrapped_app
await app(scope, receive, sender)
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\starlette\routing.py", line 73, in app
response = await f(request)
^^^^^^^^^^^^^^^^
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\fastapi\routing.py", line 301, in app
raw_response = await run_endpoint_function(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...<3 lines>...
)
^
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\fastapi\routing.py", line 212, in run_endpoint_function
return await dependant.call(**values)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\google\adk\cli\fast_api.py", line 837, in get_event_graph
session = await session_service.get_session(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
app_name=app_name, user_id=user_id, session_id=session_id
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\google\adk\sessions\in_memory_session_service.py", line 118, in get_session
return self._get_session_impl(
~~~~~~~~~~~~~~~~~~~~~~^
app_name=app_name,
^^^^^^^^^^^^^^^^^^
...<2 lines>...
config=config,
^^^^^^^^^^^^^^
)
^
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\google\adk\sessions\in_memory_session_service.py", line 157, in _get_session_impl
copied_session = copy.deepcopy(session)
File "C:\Program Files\Python313\Lib\copy.py", line 144, in deepcopy
y = copier(memo)
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\pydantic\main.py", line 940, in __deepcopy__
_object_setattr(m, '__dict__', deepcopy(self.__dict__, memo=memo))
~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Program Files\Python313\Lib\copy.py", line 137, in deepcopy
y = copier(x, memo)
File "C:\Program Files\Python313\Lib\copy.py", line 222, in _deepcopy_dict
y[deepcopy(key, memo)] = deepcopy(value, memo)
~~~~~~~~^^^^^^^^^^^^^
File "C:\Program Files\Python313\Lib\copy.py", line 137, in deepcopy
y = copier(x, memo)
File "C:\Program Files\Python313\Lib\copy.py", line 197, in _deepcopy_list
append(deepcopy(a, memo))
~~~~~~~~^^^^^^^^^
File "C:\Program Files\Python313\Lib\copy.py", line 144, in deepcopy
y = copier(memo)
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\pydantic\main.py", line 940, in __deepcopy__
_object_setattr(m, '__dict__', deepcopy(self.__dict__, memo=memo))
~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Program Files\Python313\Lib\copy.py", line 137, in deepcopy
y = copier(x, memo)
File "C:\Program Files\Python313\Lib\copy.py", line 222, in _deepcopy_dict
y[deepcopy(key, memo)] = deepcopy(value, memo)
~~~~~~~~^^^^^^^^^^^^^
File "C:\Program Files\Python313\Lib\copy.py", line 144, in deepcopy
y = copier(memo)
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\pydantic\main.py", line 940, in __deepcopy__
_object_setattr(m, '__dict__', deepcopy(self.__dict__, memo=memo))
~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Program Files\Python313\Lib\copy.py", line 137, in deepcopy
y = copier(x, memo)
File "C:\Program Files\Python313\Lib\copy.py", line 222, in _deepcopy_dict
y[deepcopy(key, memo)] = deepcopy(value, memo)
~~~~~~~~^^^^^^^^^^^^^
File "C:\Program Files\Python313\Lib\copy.py", line 137, in deepcopy
y = copier(x, memo)
File "C:\Program Files\Python313\Lib\copy.py", line 197, in _deepcopy_list
append(deepcopy(a, memo))
~~~~~~~~^^^^^^^^^
File "C:\Program Files\Python313\Lib\copy.py", line 144, in deepcopy
y = copier(memo)
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\pydantic\main.py", line 940, in __deepcopy__
_object_setattr(m, '__dict__', deepcopy(self.__dict__, memo=memo))
~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Program Files\Python313\Lib\copy.py", line 137, in deepcopy
y = copier(x, memo)
File "C:\Program Files\Python313\Lib\copy.py", line 222, in _deepcopy_dict
y[deepcopy(key, memo)] = deepcopy(value, memo)
~~~~~~~~^^^^^^^^^^^^^
File "C:\Program Files\Python313\Lib\copy.py", line 144, in deepcopy
y = copier(memo)
File "C:\Users\fbellidopazo\Envs\a2a-adk\Lib\site-packages\pydantic\main.py", line 940, in __deepcopy__
_object_setattr(m, '__dict__', deepcopy(self.__dict__, memo=memo))
~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Program Files\Python313\Lib\copy.py", line 137, in deepcopy
y = copier(x, memo)
File "C:\Program Files\Python313\Lib\copy.py", line 222, in _deepcopy_dict
y[deepcopy(key, memo)] = deepcopy(value, memo)
~~~~~~~~^^^^^^^^^^^^^
File "C:\Program Files\Python313\Lib\copy.py", line 137, in deepcopy
y = copier(x, memo)
File "C:\Program Files\Python313\Lib\copy.py", line 222, in _deepcopy_dict
y[deepcopy(key, memo)] = deepcopy(value, memo)
~~~~~~~~^^^^^^^^^^^^^
File "C:\Program Files\Python313\Lib\copy.py", line 152, in deepcopy
rv = reductor(4)
TypeError: cannot pickle 'async_generator' object