Bug Description
The AgentGraph destructor (__del__) calls asyncio.run(self.close_pool()). In an asynchronous environment (such as a Chainlit or FastAPI server), if the agent is deleted while an event loop is already running, asyncio.run() raises a RuntimeError: This event loop is already running.
Since the destructor fails mid-execution, the connection pool is never closed, leading to hanging database connections and potential pool exhaustion in long-running deployments.
Minimal Reproduction
import asyncio
from agent.graph import AgentGraph
async def reproduce():
graph = AgentGraph(profiles=["react_to_me"])
await graph.initialize()
del graph # Triggers __del__ while loop is running
# Result: RuntimeError and postgres connection remains open
asyncio.run(reproduce())
Why It's Non-Obvious
Python's __del__ is called by the garbage collector with no awareness of the async context. There is no exception raised at the call site — the RuntimeError appears only in logs, making this invisible during development and only visible under production load when connection counts begin climbing.
Fix
Update the destructor to be loop-aware:
def __del__(self) -> None:
if self.pool:
try:
loop = asyncio.get_running_loop()
if loop.is_running():
loop.create_task(self.close_pool())
else:
asyncio.run(self.close_pool())
except RuntimeError:
asyncio.run(self.close_pool())
Note: loop.create_task() during __del__ is best-effort —
if the event loop closes before the task executes, cleanup may
still be skipped. The robust long-term solution is explicit
lifecycle management via a shutdown() method called by the
application, rather than relying on __del__ at all.
Files Affected
Bug Description
The
AgentGraphdestructor (__del__) callsasyncio.run(self.close_pool()). In an asynchronous environment (such as a Chainlit or FastAPI server), if the agent is deleted while an event loop is already running,asyncio.run()raises aRuntimeError: This event loop is already running.Since the destructor fails mid-execution, the connection pool is never closed, leading to hanging database connections and potential pool exhaustion in long-running deployments.
Minimal Reproduction
Why It's Non-Obvious
Python's
__del__is called by the garbage collector with no awareness of the async context. There is no exception raised at the call site — theRuntimeErrorappears only in logs, making this invisible during development and only visible under production load when connection counts begin climbing.Fix
Update the destructor to be loop-aware:
Files Affected
src/agent/graph.py