Environment
- hermes-agent 0.18.0, checkout at 7203898
- Windows 11 Home (10.0.26200), Python 3.11.15
- Desktop app running against local venv backend; one active TUI slash-worker session doing heavy tool work
Symptom
During a heavy agent session, the desktop UI froze for roughly a minute — no streaming, no input response. The backend logged its own diagnosis:
2026-07-04 16:23:45,237 WARNING hermes_cli.web_server: event loop stalled 6.1s (GIL pressure suspected)
2026-07-04 16:24:04,582 WARNING tui_gateway.ws: ws write slow (loop stalled >10.0s) peer=127.0.0.1:51310 — frame left in flight
2026-07-04 16:24:06,848 WARNING tui_gateway.ws: ws write slow (loop stalled >10.0s) peer=127.0.0.1:51310 — frame left in flight
... (7 consecutive ws-write-slow warnings over ~30s) ...
2026-07-04 16:24:45,308 WARNING hermes_cli.web_server: event loop stalled 51.4s (GIL pressure suspected)
A 51-second asyncio loop stall means every UI websocket, HTTP endpoint, and platform adapter sharing that loop is dead for the duration — to the user the app is locked up, and it is indistinguishable from a crash until the stall clears.
Notes
- The stall detector and "GIL pressure suspected" wording are already in the codebase, so the failure mode is evidently known; this report is a data point that real-world stalls reach the tens of seconds, not just the 6s detection floor.
- Likely cause is CPU-bound work (tool result processing, embedding, tokenization?) running on the event-loop thread or a thread that holds the GIL for long stretches.
Suggestion
Whatever CPU-heavy path can hold the loop hostage for 50+ seconds would be a good candidate for a process pool / asyncio.to_thread with chunking, or at minimum the UI could surface a "backend busy" state driven by the existing stall detector, so users don't hard-kill the app during a stall.
Environment
Symptom
During a heavy agent session, the desktop UI froze for roughly a minute — no streaming, no input response. The backend logged its own diagnosis:
A 51-second asyncio loop stall means every UI websocket, HTTP endpoint, and platform adapter sharing that loop is dead for the duration — to the user the app is locked up, and it is indistinguishable from a crash until the stall clears.
Notes
Suggestion
Whatever CPU-heavy path can hold the loop hostage for 50+ seconds would be a good candidate for a process pool /
asyncio.to_threadwith chunking, or at minimum the UI could surface a "backend busy" state driven by the existing stall detector, so users don't hard-kill the app during a stall.