From 127bc620fea71386df0907dcab96a775dd49a04e Mon Sep 17 00:00:00 2001 From: "Ash (Bug Lab)" Date: Sat, 28 Feb 2026 20:50:52 +0800 Subject: [PATCH 1/2] fix(update): proactively kill stale gateway PIDs before restart (#28332) --- src/cli/update-cli/update-command.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cli/update-cli/update-command.ts b/src/cli/update-cli/update-command.ts index abc9c0080c7..7a8895b5ffa 100644 --- a/src/cli/update-cli/update-command.ts +++ b/src/cli/update-cli/update-command.ts @@ -12,6 +12,7 @@ import { } from "../../config/config.js"; import { formatConfigIssueLines } from "../../config/issue-format.js"; import { resolveGatewayService } from "../../daemon/service.js"; +import { cleanStaleGatewayProcessesSync } from "../../infra/restart-stale-pids.js"; import { channelToNpmTag, DEFAULT_GIT_CHANNEL, @@ -589,6 +590,10 @@ async function maybeRestartService(params: { } } } + // Proactively kill any stale gateway processes (e.g. bare-process nohup gateways) + // holding the port before we attempt the restart. Without this, the new process + // fails to bind the port and openclaw update leaves two conflicting gateway PIDs. + cleanStaleGatewayProcessesSync(); if (params.restartScriptPath) { await runRestartScript(params.restartScriptPath); restartInitiated = true; From f7760757fde0afc80a73acd54deb61589dbf7714 Mon Sep 17 00:00:00 2001 From: "Ash (Bug Lab)" Date: Fri, 6 Mar 2026 10:25:56 +0530 Subject: [PATCH 2/2] fix(update): guard stale-PID cleanup behind viable restart path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses Codex P1 review: cleanStaleGatewayProcessesSync() ran unconditionally before the restart strategy was resolved. When using nohup (no managed service, restartScriptPath == null) runDaemonRestart() returns false, leaving the gateway killed with no replacement — a regression from pre-change behaviour. Fix: move the proactive PID cleanup inside the restartScriptPath branch where a managed service script guarantees a replacement is coming. The daemon-restart fallback path skips the proactive cleanup; the existing post-restart health check (waitForGatewayHealthyRestart) already handles stale PIDs after the new gateway is confirmed healthy. --- src/cli/update-cli/update-command.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/cli/update-cli/update-command.ts b/src/cli/update-cli/update-command.ts index 7a8895b5ffa..ec04f2dd876 100644 --- a/src/cli/update-cli/update-command.ts +++ b/src/cli/update-cli/update-command.ts @@ -590,14 +590,20 @@ async function maybeRestartService(params: { } } } - // Proactively kill any stale gateway processes (e.g. bare-process nohup gateways) - // holding the port before we attempt the restart. Without this, the new process - // fails to bind the port and openclaw update leaves two conflicting gateway PIDs. - cleanStaleGatewayProcessesSync(); if (params.restartScriptPath) { + // A managed service restart script is available: kill stale bare-process + // gateway PIDs first so the service can bind the port on the way up. + // We only do this when we have a guaranteed restart path — killing the + // live gateway without a viable replacement would leave it down. + cleanStaleGatewayProcessesSync(); await runRestartScript(params.restartScriptPath); restartInitiated = true; } else { + // No restart script — fall back to daemon restart. Skip the proactive + // PID cleanup here: if the daemon is not loaded runDaemonRestart returns + // false and we would kill the live gateway with no replacement. The + // post-restart health check (waitForGatewayHealthyRestart) already + // handles stale PID cleanup once the new gateway is confirmed healthy. restarted = await runDaemonRestart(); }