From 67b56cba74f2cba84901a1682cbf533561f1ff36 Mon Sep 17 00:00:00 2001 From: dajiaohuang Date: Sat, 27 Jun 2026 03:54:20 +0800 Subject: [PATCH] @ fix(gateway): self-heal state file PID on server startup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After /restart on Windows, the gateway process gets a new PID (os.execv creates a new process on Windows), but the state file at run/gateway..json still contains the old PID from the initial background spawn. Nothing rewrites it, leaving state inconsistent. Add GatewayRuntime.refresh_state_pid() — a classmethod that reads the existing state file, updates the PID and identity to the current process, and writes atomically. Call it early in _run_gateway() so the state file is always correct regardless of how the process was started (initial spawn, os.execv, or subprocess.Popen). On POSIX os.execv preserves the PID, so this is a no-op in normal operation there, but still beneficial after any unusual restart path. Fixes #4511 @ --- nanobot/cli/commands.py | 11 +++++++++++ nanobot/gateway/runtime.py | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/nanobot/cli/commands.py b/nanobot/cli/commands.py index d04dd52e..a8fa81ab 100644 --- a/nanobot/cli/commands.py +++ b/nanobot/cli/commands.py @@ -1306,6 +1306,17 @@ def _run_gateway( raise typer.Exit(1) from exc session_manager = SessionManager(config.workspace_path) + # Self-heal the gateway state file with the current PID after any restart. + from nanobot.gateway.runtime import GatewayRuntime, GatewayRuntimePaths + GatewayRuntime.refresh_state_pid( + paths=GatewayRuntimePaths.for_instance( + workspace=str(config.workspace_path) + if not is_default_workspace(config.workspace_path) + else None, + config_path=config.config_path, + ) + ) + # Preserve existing single-workspace installs, but keep custom workspaces clean. if is_default_workspace(config.workspace_path): _migrate_cron_store(config) diff --git a/nanobot/gateway/runtime.py b/nanobot/gateway/runtime.py index 7acd878f..bff59f5c 100644 --- a/nanobot/gateway/runtime.py +++ b/nanobot/gateway/runtime.py @@ -127,6 +127,25 @@ class GatewayRuntime: self._subprocess_run = subprocess_run self._sleep = sleep + @classmethod + def refresh_state_pid(cls, *, paths: GatewayRuntimePaths) -> None: + """Update the PID in an existing state file to ``os.getpid()``. + + Called early in gateway server startup so the state file self-heals + after any restart, regardless of platform or restart mechanism. + """ + if not paths.state_path.exists(): + return + try: + state = json.loads(paths.state_path.read_text(encoding="utf-8")) + except (json.JSONDecodeError, OSError): + return + state["pid"] = os.getpid() + rt = cls(paths=paths) + state["identity"] = rt._process_identity(os.getpid()) + state["started_at"] = _utc_now() + rt._write_state(state) + def start_background(self, options: GatewayStartOptions) -> RuntimeResult: """Start gateway as a detached background process.""" current = self.status()