From 5987b24314b3f40043ba150819ff698bce220db0 Mon Sep 17 00:00:00 2001 From: zccyman <16263913+zccyman@users.noreply.github.com> Date: Mon, 18 May 2026 20:03:13 -0700 Subject: [PATCH] fix(gateway): exit code 75 on service restart so launchd relaunches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the gateway receives SIGUSR1 (graceful restart via launchd_restart), the SIGUSR1 handler calls request_restart(via_service=True) and the gateway shuts down cleanly with exit code 0. However, the generated launchd plist uses KeepAlive → SuccessfulExit → false, meaning launchd only relaunches on *non-zero* exit codes. A clean exit(0) is treated as "successful, don't restart", so the gateway stays down after /restart, /update, or SIGUSR1. The systemd unit template already uses RestartForceExitStatus=75 for the same scenario. Mirror that convention: when _restart_via_service is True, raise SystemExit(75) so launchd's SuccessfulExit=false policy triggers a relaunch. Closes #28135 --- gateway/run.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/gateway/run.py b/gateway/run.py index 33a16d883bd..b98857ffe52 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -17346,6 +17346,19 @@ async def start_gateway(config: Optional[GatewayConfig] = None, replace: bool = ) return False # → sys.exit(1) in the caller + # When the gateway is restarting via the service manager (SIGUSR1 → + # launchd_restart or /restart / /update commands), exit with code 75 so + # that launchd's ``KeepAlive → SuccessfulExit → false`` policy treats + # the exit as *unsuccessful* and relaunches the service. This mirrors + # the systemd ``RestartForceExitStatus=75`` convention already used by + # the systemd unit template. + if runner._restart_via_service: + logger.info( + "Exiting with code 75 (service-restart requested) so " + "launchd KeepAlive relaunches the gateway." + ) + raise SystemExit(75) + return True