Repository navigation
fix: prevent RuntimeError: Event loop is closed for Gemini.api_client (#5538) - #5543
weiguangli-io wants to merge 2 commits into
Conversation
In multi-threaded deployments (e.g. Vertex AI Agent Engine) each incoming request runs in a fresh OS thread with its own asyncio event loop. When the same Gemini instance is reused across requests the previously cached google.genai.Client (and its underlying aiohttp / httpx session) is bound to the old, now-closed event loop, causing: RuntimeError: Event loop is closed Replace @cached_property on api_client and _live_api_client with a regular @Property that caches the Client keyed by the current running event loop. Each distinct event loop (i.e. each new thread) receives a fresh Client with a fresh HTTP session, while calls within the same loop still reuse the cached instance. No public API is changed. Fixes: google#5538
|
nice im waiting on this change to be resolved.. thanks for submitting the PR |
|
Hi! The CI failure is caused by the existing tests using The fix is to replace those mocks with Happy to open a separate PR with the test fixes if that would help! |
|
Hi @weiguangli-io , Thank you for your contribution! We appreciate you taking the time to submit this pull request. Please fix the failing tests. |
|
the tests have been red since 30 april, so this can't be applied as it stands. closing on that, not on the fix. #5538 stays open and api_client is still a cached_property on main, so nothing has overtaken you. the ci break was narrowed down here: the tests patch api_client as an instance attribute and need to patch google.genai.Client instead. the loop-keyed property was sound — send a fresh pull request off current main if you want it. |
Problem
In multi-threaded deployments (e.g. Vertex AI Agent Engine), each incoming
request runs in a fresh OS thread with its own asyncio event loop. When the
same
Geminiinstance is reused across requests, the previously@cached_propertyapi_client(agoogle.genai.Client) is bound to theold, now-closed event loop via its underlying aiohttp / httpx session:
Root cause:
@cached_propertystores the firstClientcreated permanently onthe instance, regardless of which event loop is currently running.
Fixes #5538.
Solution
Replace
@cached_propertyonapi_clientand_live_api_clientwith aregular
@propertythat stores a(loop, client)pair in__dict__. On eachaccess:
(no extra allocation on the hot path).
Clientand cache it for the current loop.This means each OS thread's event loop gets an isolated
Clientinstance withits own HTTP session, while calls within the same loop still share one client.
No public API is changed. Subclasses that override
api_clientwith@cached_propertycontinue to work; the docstring hint to use@propertyinstead has been removed as the base class now handles this automatically.
Testing
test_api_client_cached_per_event_loop: two threads each run their ownevent loop and share a single
Geminiinstance; asserts they receive distinctClientobjects.test_api_client_cached_within_same_event_loop: asserts a singleClientis reused for repeated calls within one event loop.