From 019ddda13845e816aaf728924fbe4232565e07cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Han?= Date: Mon, 21 Jul 2025 20:35:15 +0200 Subject: [PATCH] fix: graceful SIGINT on server (#2831) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # What does this PR do? After https://github.com/meta-llama/llama-stack/pull/2818, SIGINT will print a stack trace. This is because uvicorn re-raises SIGINT and it gets converted by Python internal signal handler (default handles SIGINT) to KeyboardInterrupt exception. We know simply catch the exception to get a clean exit, this is not changing the behavior on SIGINT. ## Test Plan Run the server, hit Ctrl+C or `kill -2 ` and expect a clean exit with no stack trace. Signed-off-by: Sébastien Han --- llama_stack/distribution/server/server.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/llama_stack/distribution/server/server.py b/llama_stack/distribution/server/server.py index e7e9e5e88..935688946 100644 --- a/llama_stack/distribution/server/server.py +++ b/llama_stack/distribution/server/server.py @@ -597,7 +597,23 @@ def main(args: argparse.Namespace | None = None): uvicorn_config.update(ssl_config) # Run uvicorn in the existing event loop to preserve background tasks - loop.run_until_complete(uvicorn.Server(uvicorn.Config(**uvicorn_config)).serve()) + # We need to catch KeyboardInterrupt because uvicorn's signal handling + # re-raises SIGINT signals using signal.raise_signal(), which Python + # converts to KeyboardInterrupt. Without this catch, we'd get a confusing + # stack trace when using Ctrl+C or kill -2 (SIGINT). + # SIGTERM (kill -15) works fine without this because Python doesn't + # have a default handler for it. + # + # Another approach would be to ignore SIGINT entirely - let uvicorn handle it through its own + # signal handling but this is quite intrusive and not worth the effort. + try: + loop.run_until_complete(uvicorn.Server(uvicorn.Config(**uvicorn_config)).serve()) + except (KeyboardInterrupt, SystemExit): + logger.info("Received interrupt signal, shutting down gracefully...") + finally: + if not loop.is_closed(): + logger.debug("Closing event loop") + loop.close() def _log_run_config(run_config: StackRunConfig):