Skip to content

fix: guard against missing _async_httpx_client in aclose()#2297

Open
jrcks67 wants to merge 1 commit intogoogleapis:mainfrom
jrcks67:fix/aclose-missing-async-httpx-client
Open

fix: guard against missing _async_httpx_client in aclose()#2297
jrcks67 wants to merge 1 commit intogoogleapis:mainfrom
jrcks67:fix/aclose-missing-async-httpx-client

Conversation

@jrcks67
Copy link
Copy Markdown

@jrcks67 jrcks67 commented Apr 17, 2026

Problem

When BaseApiClient.__init__ raises before _async_httpx_client is assigned (e.g. an auth error thrown before any HTTP call is made), __del__ still schedules aclose() as an asyncio task. That task then crashes with:

AttributeError: 'BaseApiClient' object has no attribute '_async_httpx_client'
Task exception was never retrieved
future: <Task finished name='Task-...' coro=<BaseApiClient.aclose()> exception=AttributeError(...)>

The exception surfaces as an unhandled asyncio task exception — swallowed silently in production unless PYTHONASYNCIODEBUG=1 is set, making it hard to spot.

Minimal repro (v1.66.0)

import asyncio, os
os.environ.pop("GOOGLE_API_KEY", None)
from google import genai
client = genai.Client(api_key="")
# trigger any async call — the __del__ task fires after GC and crashes
asyncio.run(client.aio.models.generate_content("gemini-2.0-flash", contents="hi"))

Traceback:

Task exception was never retrieved
future: <Task finished name='Task-18' coro=<BaseApiClient.aclose() done, defined at .../google/genai/_api_client.py:2100> exception=AttributeError("'BaseApiClient' object has no attribute '_async_httpx_client'")>
  File ".../google/genai/_api_client.py", line 2105, in aclose
    await self._async_httpx_client.aclose()
AttributeError: 'BaseApiClient' object has no attribute '_async_httpx_client'

Fix

close() already guards the equivalent sync path with and self._httpx_client. Apply the same pattern to aclose() using getattr(..., None) to safely handle the case where _async_httpx_client was never set:

# before
if not self._http_options.httpx_async_client:
    await self._async_httpx_client.aclose()

# after
if not self._http_options.httpx_async_client and getattr(self, '_async_httpx_client', None):
    await self._async_httpx_client.aclose()

One-line change, no behaviour difference in the normal path.

When __init__ raises before _async_httpx_client is assigned (e.g. an
auth error thrown before any HTTP call), __del__ still schedules
aclose() as an asyncio task. aclose() then crashes with:

  AttributeError: 'BaseApiClient' object has no attribute '_async_httpx_client'

The error surfaces as an unhandled asyncio task exception, which is
swallowed silently unless asyncio debug mode is on.

close() already guards the equivalent sync path with
`and self._httpx_client`; apply the same pattern to aclose() using
getattr(..., None) to safely handle the uninitialized case.

Fixes the unhandled task exception reported when using the async client
with a missing or invalid API key.
@google-cla
Copy link
Copy Markdown

google-cla Bot commented Apr 17, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@Venkaiahbabuneelam Venkaiahbabuneelam self-assigned this Apr 21, 2026
@Venkaiahbabuneelam Venkaiahbabuneelam added the size:S Code changes < 10 lines label Apr 21, 2026
@Venkaiahbabuneelam
Copy link
Copy Markdown

Hi @jrcks67, Thanks for reaching out us!

It looks like some checks failed. Kindly resolve the conflicts.

Thanks

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants