Bug Description
tools/homeassistant_tool.py reuses persistent event loops for some sync call paths, but _run_async() still falls back to a disposable-thread + asyncio.run(coro) path when called from inside an already-running event loop.
In long-running Hermes processes, that per-call create-and-destroy loop behavior can leave aiohttp cleanup tied to dead loops and risks Unclosed client session / Unclosed connector warnings.
Steps to Reproduce
- Start from current
main.
- Call
tools/homeassistant_tool.py:_run_async() from within an already-running event loop.
- Exercise a Home Assistant tool path that creates and cleans up an async HTTP client/session.
- Observe that this branch still creates a fresh event loop via
asyncio.run() for each call instead of using a persistent loop.
Expected Behavior
When _run_async() is called from inside a running event loop, the Home Assistant tool should use a persistent async bridge loop so async client cleanup stays bound to a live event loop.
Actual Behavior
The async-context branch creates a disposable thread and runs asyncio.run(coro) per invocation, which creates and immediately closes a fresh event loop for that call.
Environment
Error Output
Observed risk/symptom in long-running processes:
ERROR asyncio: Unclosed client session
ERROR asyncio: Unclosed connector
Relevant problematic pattern:
if loop and loop.is_running():
import concurrent.futures
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
future = pool.submit(asyncio.run, coro)
return future.result(timeout=30)
Additional Context
This issue is narrowly scoped to tools/homeassistant_tool.py.
It is related to async resource lifecycle management and the same general class of sync-to-async bridging problems seen elsewhere in Hermes, but I did not find an existing open issue that cleanly covers this exact Home Assistant tool path.
A proposed fix is in PR #13422:
#13422
Bug Description
tools/homeassistant_tool.pyreuses persistent event loops for some sync call paths, but_run_async()still falls back to a disposable-thread +asyncio.run(coro)path when called from inside an already-running event loop.In long-running Hermes processes, that per-call create-and-destroy loop behavior can leave
aiohttpcleanup tied to dead loops and risksUnclosed client session/Unclosed connectorwarnings.Steps to Reproduce
main.tools/homeassistant_tool.py:_run_async()from within an already-running event loop.asyncio.run()for each call instead of using a persistent loop.Expected Behavior
When
_run_async()is called from inside a running event loop, the Home Assistant tool should use a persistent async bridge loop so async client cleanup stays bound to a live event loop.Actual Behavior
The async-context branch creates a disposable thread and runs
asyncio.run(coro)per invocation, which creates and immediately closes a fresh event loop for that call.Environment
mainbefore PR fix(tools): reuse persistent loop in Home Assistant async bridge #13422Error Output
Observed risk/symptom in long-running processes:
Relevant problematic pattern:
Additional Context
This issue is narrowly scoped to
tools/homeassistant_tool.py.It is related to async resource lifecycle management and the same general class of sync-to-async bridging problems seen elsewhere in Hermes, but I did not find an existing open issue that cleanly covers this exact Home Assistant tool path.
A proposed fix is in PR #13422:
#13422