Skip to content

Async client garbage collection silently skips connection cleanup without a running loop #356

Description

@abhinavkr26104

Description

The README says underlying HTTP connections are closed when the client is garbage collected, but the async HTTP wrapper can only schedule aclose() when garbage collection happens inside a running event loop.

If an AsyncStagehand client becomes unreachable after asyncio.run() returns (or in other code with no active loop), AsyncHttpxClientWrapper.__del__ catches RuntimeError from asyncio.get_running_loop() and silently does nothing. The connection pool is left unclosed.

There is already a TODO in the implementation noting that only asyncio runtimes are supported, but the no-running-loop case is also unhandled.

Reproduction

import gc
import asyncio

from stagehand import AsyncStagehand

async def create_client():
    return AsyncStagehand()

client = asyncio.run(create_client())
del client
gc.collect()  # runs after asyncio.run() closed its loop

At src/stagehand/_base_client.py:1416-1424, the finalizer attempts:

asyncio.get_running_loop().create_task(self.aclose())

There is no running loop at gc.collect(), and the broad except Exception: pass suppresses the failure without closing or warning.

Expected behavior

Either async clients should have a reliable cleanup strategy for this case, or the finalizer should emit a ResourceWarning and the documentation should clearly state that async clients are not automatically closed by garbage collection and must use async with / await close().

Actual behavior

Cleanup is silently skipped, contradicting the unconditional garbage-collection claim in README.md:678-680.

Why this matters

Unclosed async clients retain connection pools and sockets. Silence makes leaks hard to diagnose, particularly in test runners, notebooks, and applications that construct a client in one event-loop lifetime and release it later.

Prior-art check

I searched open/closed issues and PRs for unclosed AsyncStagehand, garbage collection, AsyncHttpxClientWrapper, and missing running loops and found no existing report.

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