openclaw - 💡(How to fix) Fix Updater submits manual-update job via `launchctl submit` → KeepAlive defaults true → infinite update loop that repeatedly stops the gateway

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

On macOS, openclaw update schedules its out-of-process update job using launchctl submit. launchctl submit creates the job with KeepAlive = true by default, so the update runner (run-openclaw-update.zsh) is relaunched immediately every time it exits. Each iteration runs openclaw update, whose first action is "Stopping managed gateway service before package update" — so the managed gateway is killed roughly once per minute and never stays up. The package update itself succeeds on the first pass; the loop continues forever afterward with availability.available = false.

Net user-visible symptom: after updating, the gateway won't stay up.

Root Cause

The submitted launchd jobs are KeepAlive. Confirmed via:

$ launchctl print gui/501/ai.openclaw.manual-update.<epoch>
    path = (submitted by launchctl[...])
    program = /Users/<user>/.openclaw/work/update-logs/run-openclaw-update.zsh
    properties = keepalive | inferred program

path = (submitted by launchctl ...) + properties = keepalive = a launchctl submit job using the default unconditional KeepAlive. run-openclaw-update.zsh exits cleanly (DOCTOR_EXIT 0) each cycle, and launchd relaunches it in the same second:

== END   Sun May 31 19:49:38 SAST 2026 ==
== START Sun May 31 19:49:38 SAST 2026 ==

Fix Action

Fix / Workaround

Workaround (for affected users)

# tear down the runaway loop
for l in $(launchctl list | awk '/ai\.openclaw\.manual-update/ {print $3}'); do launchctl bootout gui/$(id -u)/$l; done
pkill -f run-openclaw-update.zsh; pkill -f openclaw-doctor
# the gateway is left booted out — reload it
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.openclaw.gateway.plist
launchctl kickstart -k gui/$(id -u)/ai.openclaw.gateway

The submitted jobs are not persisted to disk, so they don't survive logout/reboot — but the next update re-triggers the loop until this is fixed.

Code Example

$ launchctl print gui/501/ai.openclaw.manual-update.<epoch>
    path = (submitted by launchctl[...])
    program = /Users/<user>/.openclaw/work/update-logs/run-openclaw-update.zsh
    properties = keepalive | inferred program

---

== END   Sun May 31 19:49:38 SAST 2026 ==
== START Sun May 31 19:49:38 SAST 2026 ==

---

# tear down the runaway loop
for l in $(launchctl list | awk '/ai\.openclaw\.manual-update/ {print $3}'); do launchctl bootout gui/$(id -u)/$l; done
pkill -f run-openclaw-update.zsh; pkill -f openclaw-doctor
# the gateway is left booted out — reload it
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.openclaw.gateway.plist
launchctl kickstart -k gui/$(id -u)/ai.openclaw.gateway
RAW_BUFFERClick to expand / collapse

Summary

On macOS, openclaw update schedules its out-of-process update job using launchctl submit. launchctl submit creates the job with KeepAlive = true by default, so the update runner (run-openclaw-update.zsh) is relaunched immediately every time it exits. Each iteration runs openclaw update, whose first action is "Stopping managed gateway service before package update" — so the managed gateway is killed roughly once per minute and never stays up. The package update itself succeeds on the first pass; the loop continues forever afterward with availability.available = false.

Net user-visible symptom: after updating, the gateway won't stay up.

Environment

  • OpenClaw 2026.5.28 (commit e932160); first observed after auto-update 2026.5.26 → 2026.5.28
  • Install: global package at /opt/homebrew/lib/node_modules/openclaw (pnpm)
  • node v25.6.1
  • macOS 26.4 (arm64)

Impact

  • Managed gateway (ai.openclaw.gateway) is force-stopped ~1×/minute indefinitely → all agents/channels (Discord, Telegram, cron) keep dropping.
  • Two jobs were submitted ~1s apart (ai.openclaw.manual-update.<epoch>), so the gateway was being stopped by two concurrent loops.
  • ~/.openclaw/work/update-logs/openclaw-update-*.log grew to ~2 MB / 80+ repeated == START / == UPDATE / == DOCTOR cycles in ~80 min.
  • ~/.openclaw/logs/gateway-restart.log fills with Could not find service "ai.openclaw.gateway" in domain for user gui: 501 as the loop races with itself (kickstart attempted against a service that a concurrent iteration has just booted out).

Root cause

The submitted launchd jobs are KeepAlive. Confirmed via:

$ launchctl print gui/501/ai.openclaw.manual-update.<epoch>
    path = (submitted by launchctl[...])
    program = /Users/<user>/.openclaw/work/update-logs/run-openclaw-update.zsh
    properties = keepalive | inferred program

path = (submitted by launchctl ...) + properties = keepalive = a launchctl submit job using the default unconditional KeepAlive. run-openclaw-update.zsh exits cleanly (DOCTOR_EXIT 0) each cycle, and launchd relaunches it in the same second:

== END   Sun May 31 19:49:38 SAST 2026 ==
== START Sun May 31 19:49:38 SAST 2026 ==

Steps to reproduce

  1. On macOS, trigger an update that uses the launchctl-submit path (e.g. auto-update or openclaw update).
  2. Observe a transient ai.openclaw.manual-update.<epoch> job appear: launchctl list | grep manual-update.
  3. launchctl print gui/$(id -u)/ai.openclaw.manual-update.<epoch>properties = keepalive.
  4. Watch the gateway get stopped each cycle (gateway-restart.log / update log) and never stay up.

Expected

The update job runs once and exits. The gateway is stopped only for the actual package swap, then restarted and left running.

Suggested fix

launchctl submit is deprecated and defaults to unconditional KeepAlive — it's the wrong primitive for a run-once task. Options:

  1. Preferred: write a temporary LaunchAgent plist with KeepAlive = false (or omit it) and RunAtLoad = true, bootstrap it, and bootout it on completion — instead of launchctl submit.
  2. If staying with launchctl submit, the runner must remove its own job on exit (e.g. launchctl remove ai.openclaw.manual-update.<epoch> as the last step) so it can't be relaunched.
  3. Defensive: never submit a second update job while one is already registered (the two near-simultaneous submits doubled the damage).
  4. The "stop the gateway before update" step should be gated on an update actually being applied — when availability.available == false, the job should no-op instead of stopping the gateway.

Workaround (for affected users)

# tear down the runaway loop
for l in $(launchctl list | awk '/ai\.openclaw\.manual-update/ {print $3}'); do launchctl bootout gui/$(id -u)/$l; done
pkill -f run-openclaw-update.zsh; pkill -f openclaw-doctor
# the gateway is left booted out — reload it
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.openclaw.gateway.plist
launchctl kickstart -k gui/$(id -u)/ai.openclaw.gateway

The submitted jobs are not persisted to disk, so they don't survive logout/reboot — but the next update re-triggers the loop until this is fixed.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix Updater submits manual-update job via `launchctl submit` → KeepAlive defaults true → infinite update loop that repeatedly stops the gateway