From a9af49df0936fc8af45ba7542e19e3c1274835ef Mon Sep 17 00:00:00 2001 From: JonthanaHanh <92574114+JonthanaHanh@users.noreply.github.com> Date: Tue, 28 Jul 2026 17:34:39 +0700 Subject: [PATCH] fix(web-server): absorb _warm_gateway_module import before lifespan yield (#73083) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Windows + Python 3.11 the gateway import triggers heavy .pyc compilation and Defender real-time scans that do not release the GIL. Running in run_in_executor still froze the event loop for 15-22 s, causing the Desktop's 10-second WebSocket ready-probe to time out. Move the call from the executor to a synchronous invocation before the lifespan yield, so the GIL block is absorbed during backend initialisation — before the server socket accepts probes. Fixes #73083 --- hermes_cli/web_server.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/hermes_cli/web_server.py b/hermes_cli/web_server.py index 0c36bbb6dc8b..f2d4ea119303 100644 --- a/hermes_cli/web_server.py +++ b/hermes_cli/web_server.py @@ -195,13 +195,13 @@ async def _lifespan(app: "FastAPI"): # event loop during lifespan startup — see _get_event_state's docstring. app.state.chat_argv_lock = asyncio.Lock() - # Fire hermes_cli.gateway import into a background thread so the event - # loop is not blocked and HERMES_DASHBOARD_READY fires without delay. - # On a cold Windows install the module chain triggers .pyc compilation - # and Defender real-time scans that can stall the event loop for 15-30s. - # Running in an executor means the cost is paid in a worker thread while - # the server socket is already open and accepting probes. - asyncio.get_event_loop().run_in_executor(None, _warm_gateway_module) + # Import hermes_cli.gateway eagerly *before* the lifespan yield so the + # GIL-heavy .pyc compilation and Defender scan cost is absorbed during + # backend initialisation — before the server socket accepts probes. + # On Windows + Python 3.11 the import does not release the GIL, so + # run_in_executor still froze the event loop for 15-22 s, causing the + # Desktop's 10-second WebSocket ready-probe to time out (GH-73083). + _warm_gateway_module() # Desktop-spawned backends (HERMES_DESKTOP=1) fire cron jobs themselves, # since the app has no gateway running the scheduler. Server `hermes