Problem
During simulation, stderr is spammed with httpx cleanup warnings:
ERROR Task exception was never retrieved
future: <Task finished name='Task-177' coro=<AsyncClient.aclose() done, ...> exception=RuntimeError('Event loop is closed')>
Traceback (most recent call last):
File ".../httpx/_client.py", line 1985, in aclose
await self._transport.aclose()
...
File ".../asyncio/base_events.py", line 545, in _check_closed
raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
These appear hundreds of times during a simulation run.
Root Cause
The async httpx clients created by the OpenAI/Anthropic SDKs are not being properly closed before the event loop shuts down. When Python's asyncio loop closes, pending aclose() tasks fail because they try to schedule work on a closed loop.
Impact
- Functional: None - simulation completes correctly
- UX: Noisy stderr, looks like errors
- Debugging: Hard to spot real errors in the noise
Potential Fixes
-
Explicit client lifecycle: Create a single shared httpx.AsyncClient at simulation start, close it explicitly before loop shutdown
-
Context manager pattern: Wrap simulation in async context that ensures cleanup:
async with create_llm_client() as client:
await run_simulation(client, ...)
-
Suppress warnings: Filter these specific warnings (not ideal but pragmatic)
-
Use atexit or signal handlers: Register cleanup before loop close
Files to Investigate
extropy/core/llm.py - where async LLM calls are made
extropy/core/providers/ - provider implementations
extropy/simulation/engine.py - simulation loop lifecycle
References
Problem
During simulation, stderr is spammed with httpx cleanup warnings:
These appear hundreds of times during a simulation run.
Root Cause
The async httpx clients created by the OpenAI/Anthropic SDKs are not being properly closed before the event loop shuts down. When Python's asyncio loop closes, pending
aclose()tasks fail because they try to schedule work on a closed loop.Impact
Potential Fixes
Explicit client lifecycle: Create a single shared
httpx.AsyncClientat simulation start, close it explicitly before loop shutdownContext manager pattern: Wrap simulation in async context that ensures cleanup:
Suppress warnings: Filter these specific warnings (not ideal but pragmatic)
Use
atexitor signal handlers: Register cleanup before loop closeFiles to Investigate
extropy/core/llm.py- where async LLM calls are madeextropy/core/providers/- provider implementationsextropy/simulation/engine.py- simulation loop lifecycleReferences