diff --git a/gateway/run.py b/gateway/run.py index 4201dde1ca58..1c6a8a839738 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -7823,10 +7823,24 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew Returns True if at least one adapter connected successfully. """ logger.info("Starting Hermes Gateway...") - # Enable faulthandler at gateway start so that SIGUSR2 (or an - # internal watchdog) can dump all thread and task stacks to stderr - # for post-mortem diagnosis of event-loop freezes (#70344). - faulthandler.enable() + # Enable faulthandler for stack dumps on freezes/crashes (#70344). + # Falls back to a log file when sys.stderr is None (Windows VBS / + # pythonw / detached service) — otherwise the gateway would die + # here and take every adapter offline. See #71671. + try: + faulthandler.enable() + except (RuntimeError, ValueError, OSError): + try: + _fh_log_dir = getattr(self.config, "log_dir", None) or os.path.join( + os.environ.get("HERMES_HOME", str(Path.home() / ".hermes")), + "logs", + ) + os.makedirs(_fh_log_dir, exist_ok=True) + _fh_enable_path = os.path.join(_fh_log_dir, "gateway_faulthandler.log") + _fh_enable_file = open(_fh_enable_path, "a", encoding="utf-8") + faulthandler.enable(file=_fh_enable_file, all_threads=True) + except Exception: + logger.debug("faulthandler.enable() unavailable", exc_info=True) # Also dump stacks to a rotating file for off-line analysis when # the gateway is running under a service manager that doesn't # capture stderr.