Skip to content

test(google): disarm genai client finalizers in realtime tests - #6975

Open
rkfshakti wants to merge 1 commit into
livekit:mainfrom
rkfshakti:fix/flaky-genai-finalizer-leak
Open

test(google): disarm genai client finalizers in realtime tests#6975
rkfshakti wants to merge 1 commit into
livekit:mainfrom
rkfshakti:fix/flaky-genai-finalizer-leak

Conversation

@rkfshakti

Copy link
Copy Markdown

Fixes #6881

Problem

tests/test_plugin_openai_stt_context.py::test_use_realtime_defaults_to_the_only_transport_a_model_has[gpt-realtime-whisper-True] intermittently fails with:

Failed: Test leaked tasks:
Coroutine : BaseApiClient.aclose    google/genai/_api_client.py:2213
Coroutine : AsyncClient.aclose      google/genai/client.py:152

That test is synchronous and never touches google. The tasks come from tests/test_plugin_google_realtime.py — the only module in the unit suite that constructs genai clients.

Root cause

AsyncClient.__del__ and BaseApiClient.__del__ both do this unconditionally, with no check for a client that was already closed:

def __del__(self) -> None:
    try:
      asyncio.get_running_loop().create_task(self.aclose())
    except Exception:
      pass

RealtimeSession.aclose() closes client.aio, but that does not disarm the finalizer — the object stays garbage until the collector reaches it, which can be several modules later. fail_on_leaked_tasks diffs asyncio.all_tasks() around each test, so the task is charged to whatever test happens to be running then.

Fix

Disarm both finalizers at module import in tests/test_plugin_google_realtime.py — the only module that constructs genai clients. Sessions already close their clients explicitly, so the finalizers are pure redundancy.

Verified:

  • Without the disarm, dropping a client schedules 2 pending AsyncClient.aclose tasks on the running loop
  • With the disarm, zero tasks leak
  • All 18 realtime tests pass; full unit suite passes (1735 passed)

AsyncClient.__del__ and BaseApiClient.__del__ schedule aclose() on
whatever event loop is running when the garbage collector reaches them.
Sessions in this module close their clients explicitly in
RealtimeSession.aclose(), so the finalizers are pure redundancy — but
they leak pending tasks into whichever test happens to be running at GC
time, and fail_on_leaked_tasks charges them to that test (issue livekit#6881).

Disarm both finalizers at module import, in the only module that
constructs genai clients. Verified: without the disarm, dropping a
client schedules 2 pending AsyncClient.aclose tasks; with it, zero.
@rkfshakti
rkfshakti requested a review from a team as a code owner August 25, 2026 10:35
@CLAassistant

CLAassistant commented Aug 25, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Flaky unit-tests: a genai client finalizer lands in an unrelated test's leak check

2 participants