Summary
When openclaw update --tag <ver> --yes is invoked from a process whose parent process tree includes the active gateway PID (e.g., from inside an agent's exec subprocess), the package install and doctor migrations complete successfully, but the supervised-mode restart silently fails to take effect. The binary on disk is the new version while the gateway PID and in-memory code remain the old version, leaving the operator with no signal that anything went wrong.
Reproduction
- Run OpenClaw under systemd (system-level unit at
/etc/systemd/system/openclaw.service, Restart=always)
- From inside an agent's
exec tool (gateway is the agent's parent process), run:
openclaw update --tag <newer-version> --yes
- Observe: install logs report success, doctor migrations apply, command exits 0.
- Verify in-memory state — the gateway PID is unchanged. The new code is on disk but not loaded.
Root cause
In dist/cli/gateway-lifecycle.runtime.js::restartGatewayProcessWithFreshPid(), when systemd is detected as the supervisor, the function returns { mode: "supervised" } and the caller is expected to exit. The architectural assumption is that the caller IS the gateway process — exiting triggers systemd's Restart=always to spawn the fresh process.
When the caller is a CHILD of the gateway (an agent's exec subprocess), child exit doesn't propagate to the gateway parent, systemd sees the gateway alive, and no restart fires.
Proposal
Make openclaw update walk its own ppid chain at startup. If the active gateway PID appears in the caller's ancestor PIDs:
Option A (defensive, smallest change): Refuse to perform the restart phase. Surface a clear error:
Error: openclaw update detected it is running inside the gateway process tree
(gateway PID <X> is an ancestor of this process). The supervised-mode restart
cannot fire from this context. Run `openclaw update` from an external shell
(SSH, cron, or other detached session). To install without restart from this
context, pass --no-restart and manually invoke `sudo systemctl restart <unit>`
from outside.
Option B (helpful, slightly larger change): Auto-defer the restart via a detached helper. Spawn nohup bash -c 'sleep 2 && systemctl restart <detected-unit>' &, then exit. Document that this only works if the active unit is system-level and the user has appropriate sudo.
Either solves the silent-failure mode. Option A is simpler and more conservative.
Why this matters
Operators running OpenClaw under any agent-orchestration pattern (HZL, Lobster, COO agents, etc.) will frequently invoke updates from inside the gateway-managed lane. The current behavior is a footgun: the update appears to succeed but the gateway keeps running old code. Without external monitoring, this can persist for hours or days.
Empirical evidence
From a 4.29 cutover at 2026-05-01 04:16-04:21 UTC:
- An agent (Trent) ran
openclaw update --tag 2026.4.29 --yes from its gateway-owned exec lane
- Binary on disk: 2026.4.29 (
openclaw --version confirmed)
systemctl show openclaw -p MainPID --value: 598350 (the OLD 4.27 process, unchanged)
- Recovery:
sudo systemctl restart openclaw.service from a fresh subprocess → MainPID changed to 605944
Same pattern observed on 4.26 cutover ~2026-04-28.
Workaround in place
Operating SOP "openclaw-update-native-v1" v3 now mandates external-lane invocation as primary, with in-lane B2a/B2b split (--no-restart install + external sudo systemctl restart openclaw.service) as fallback only.
Sister filing
Companion to a separate request for openclaw update to support system-level systemd units explicitly (currently the detached restart-script uses systemctl --user restart which doesn't control system-level units).
Summary
When
openclaw update --tag <ver> --yesis invoked from a process whose parent process tree includes the active gateway PID (e.g., from inside an agent'sexecsubprocess), the package install and doctor migrations complete successfully, but the supervised-mode restart silently fails to take effect. The binary on disk is the new version while the gateway PID and in-memory code remain the old version, leaving the operator with no signal that anything went wrong.Reproduction
/etc/systemd/system/openclaw.service,Restart=always)exectool (gateway is the agent's parent process), run:Root cause
In
dist/cli/gateway-lifecycle.runtime.js::restartGatewayProcessWithFreshPid(), when systemd is detected as the supervisor, the function returns{ mode: "supervised" }and the caller is expected to exit. The architectural assumption is that the caller IS the gateway process — exiting triggers systemd'sRestart=alwaysto spawn the fresh process.When the caller is a CHILD of the gateway (an agent's
execsubprocess), child exit doesn't propagate to the gateway parent, systemd sees the gateway alive, and no restart fires.Proposal
Make
openclaw updatewalk its own ppid chain at startup. If the active gateway PID appears in the caller's ancestor PIDs:Option A (defensive, smallest change): Refuse to perform the restart phase. Surface a clear error:
Option B (helpful, slightly larger change): Auto-defer the restart via a detached helper. Spawn
nohup bash -c 'sleep 2 && systemctl restart <detected-unit>' &, then exit. Document that this only works if the active unit is system-level and the user has appropriate sudo.Either solves the silent-failure mode. Option A is simpler and more conservative.
Why this matters
Operators running OpenClaw under any agent-orchestration pattern (HZL, Lobster, COO agents, etc.) will frequently invoke updates from inside the gateway-managed lane. The current behavior is a footgun: the update appears to succeed but the gateway keeps running old code. Without external monitoring, this can persist for hours or days.
Empirical evidence
From a 4.29 cutover at 2026-05-01 04:16-04:21 UTC:
openclaw update --tag 2026.4.29 --yesfrom its gateway-owned exec laneopenclaw --versionconfirmed)systemctl show openclaw -p MainPID --value: 598350 (the OLD 4.27 process, unchanged)sudo systemctl restart openclaw.servicefrom a fresh subprocess → MainPID changed to 605944Same pattern observed on 4.26 cutover ~2026-04-28.
Workaround in place
Operating SOP "openclaw-update-native-v1" v3 now mandates external-lane invocation as primary, with in-lane B2a/B2b split (
--no-restartinstall + externalsudo systemctl restart openclaw.service) as fallback only.Sister filing
Companion to a separate request for
openclaw updateto support system-level systemd units explicitly (currently the detached restart-script usessystemctl --user restartwhich doesn't control system-level units).