Skip to content

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

Description

@helin-mktech

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.

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

# google/genai/client.py:186, _api_client.py:2248
def __del__(self) -> None:
    try:
      asyncio.get_running_loop().create_task(self.aclose())
    except Exception:
      pass

RealtimeSession.aclose() closes client.aio (realtime_api.py:898), 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.

Why it surfaces now. It reproduces only on the CPython build CI uses — 3.12.13 fails every run, 3.12.3 passes every run, on the same machine with the same locked dependencies. Which test gets blamed depends on GC timing, so anything that shifts allocation can move it. It currently lands on #6340, which adds a test module and nothing else related.

Fix. Settle the finalizers inside the test that owns them, so they cannot escape into a later module.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions