[Bugfix] Fix launch render hanging on shutdown (#54913)

Signed-off-by: chaunceyjiang <chaunceyjiang@gmail.com>

Chauncey committed Sep 2, 2026 at 16:15 UTC b2558f8da330a2fde6de44ea128d3c23c5cf8268
1 file changed +22 -15
vllm/entrypoints/launchers/launcher.py
+22 -15
@@ -102,7 +102,12 @@ async def serve_http(
102
103 loop = asyncio.get_running_loop()
104
105 - watchdog_task = loop.create_task(watchdog_loop(server, app.state.engine_client))
105 + engine_client = app.state.engine_client
106 + watchdog_task = (
107 + loop.create_task(watchdog_loop(server, engine_client))
108 + if engine_client is not None
109 + else None
110 + )
111 server_task = loop.create_task(server.serve(sockets=[sock] if sock else None))
112
113 ssl_cert_refresher = (
@@ -133,25 +138,26 @@ async def serve_http(
138 async def handle_shutdown() -> None:
139 await shutdown_event.wait()
140
136 - engine_client = app.state.engine_client
137 - timeout = engine_client.vllm_config.shutdown_timeout
138 - mode = "abort" if timeout == 0 else "drain"
141 + if engine_client is not None:
142 + timeout = engine_client.vllm_config.shutdown_timeout
143 + mode = "abort" if timeout == 0 else "drain"
144
140 - logger.info(
141 - "[shutdown] API server: stopping engine client mode=%s timeout=%ss",
142 - mode,
143 - timeout,
144 - )
145 + logger.info(
146 + "[shutdown] API server: stopping engine client mode=%s timeout=%ss",
147 + mode,
148 + timeout,
149 + )
150
146 - await loop.run_in_executor(
147 - None, partial(engine_client.shutdown, timeout=timeout)
148 - )
149 - logger.info_once("[shutdown] API server: engine client stopped")
151 + await loop.run_in_executor(
152 + None, partial(engine_client.shutdown, timeout=timeout)
153 + )
154 + logger.info_once("[shutdown] API server: engine client stopped")
155
156 server.should_exit = True
157 logger.info_once("[shutdown] API server: signalling HTTP server shutdown")
158 server_task.cancel()
154 - watchdog_task.cancel()
159 + if watchdog_task is not None:
160 + watchdog_task.cancel()
161 if ssl_cert_refresher:
162 ssl_cert_refresher.stop()
163
@@ -174,7 +180,8 @@ async def serve_http(
180 return server.shutdown()
181 finally:
182 shutdown_task.cancel()
177 - watchdog_task.cancel()
183 + if watchdog_task is not None:
184 + watchdog_task.cancel()
185
186
187 async def watchdog_loop(server: uvicorn.Server, engine: EngineClient):