























@@ -107,6 +107,20 @@ export async function runGatewayLoop(params: {
107107waitForHealthyChild?: (port: number, pid?: number, host?: string) => Promise<boolean>;
108108}) {
109109let startupStartedAt = Date.now();
110+// Eagerly resolve the lifecycle runtime module before installing signal
111+// listeners. Without this, every subsequent lifecycle path (SIGUSR1,
112+// SIGTERM-with-intent, restart iteration hook, stability bundle writer)
113+// depends on a dynamic import() call. After an in-place package upgrade
114+// (e.g. `npm install -g openclaw@latest` triggered via update.run),
115+// dist/ chunk hashes rotate while the process is still running. The next
116+// SIGUSR1 — including the one update.run schedules for itself — would
117+// hit ERR_MODULE_NOT_FOUND from inside its async IIFE, reject silently,
118+// and leave restart.ts's emittedRestartToken permanently unconsumed.
119+// From that point every scheduleGatewaySigusr1Restart() returns
120+// { coalesced: true } and the gateway never restarts. Priming the loader
121+// here pulls the whole re-export graph (lifecycle.runtime.ts is a 36-line
122+// re-export hub) into memory, immune to later disk rotation.
123+const eagerLifecycleRuntime = await loadGatewayLifecycleRuntimeModule();
110124let lock = await acquireGatewayLock({ port: params.lockPort });
111125let server: Awaited<ReturnType<typeof startGatewayServer>> | null = null;
112126let shuttingDown = false;
@@ -745,7 +759,20 @@ export async function runGatewayLoop(params: {
745759const restartReason = peekGatewaySigusr1RestartReason();
746760markGatewaySigusr1RestartHandled();
747761request("restart", "SIGUSR1", restartReason);
748-})();
762+})().catch((err) => {
763+// Defense in depth: if anything in the listener body rejects, the
764+// SIGUSR1 emit has already advanced emittedRestartToken but no one
765+// called markGatewaySigusr1RestartHandled. Without unsticking the
766+// token here, every subsequent scheduleGatewaySigusr1Restart() would
767+// silently coalesce into the dead in-flight signal and the gateway
768+// would never restart again until manually kickstarted.
769+gatewayLog.error(`SIGUSR1 handler failed: ${formatErrorMessage(err)}`);
770+try {
771+eagerLifecycleRuntime.markGatewaySigusr1RestartHandled();
772+} catch {
773+// Best-effort: the eager reference itself is the recovery path.
774+}
775+});
749776};
750777751778process.on("SIGTERM", onSigterm);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。