openclaw - 💡(How to fix) Fix macOS: launchctl submit can relaunch one-shot update jobs after clean exit

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, launchctl submit is unsafe as a one-shot supervisor primitive for OpenClaw update wrappers. A submitted updater job can be inferred by launchd as keepalive, then relaunched after clean successful exits. If the worker path performs plugin sync or openclaw gateway restart after a no-op package precheck, this can create a repeated gateway restart loop even though the target OpenClaw version is already installed.

This is adjacent to #82167, but that issue is locked/closed and the new evidence is from a fresh 2026.5.22 update wrapper validation. This is also related to the broader macOS LaunchAgent lifecycle work in #82250, but the root primitive here is specifically launchctl submit for a detached/manual updater job.

Root Cause

During the 2026.5.22 update path, the worker correctly observed that the target version was already installed. However, the worker-mode no-op path still continued into plugin sync plus gateway restart. Because the submitted job was being relaunched by launchd after clean exits, that meant repeated gateway restarts even though no package update was needed.

Code Example

label = ai.openclaw.oob-update.selftest.20260521T230100Z
state = spawn scheduled / running across checks
runs = 23080+
last exit code = 0
properties = keepalive | inferred program

---

runId = 20260524T153034Z
status = noop
gatewayRestartAttempted = false
RAW_BUFFERClick to expand / collapse

Summary

On macOS, launchctl submit is unsafe as a one-shot supervisor primitive for OpenClaw update wrappers. A submitted updater job can be inferred by launchd as keepalive, then relaunched after clean successful exits. If the worker path performs plugin sync or openclaw gateway restart after a no-op package precheck, this can create a repeated gateway restart loop even though the target OpenClaw version is already installed.

This is adjacent to #82167, but that issue is locked/closed and the new evidence is from a fresh 2026.5.22 update wrapper validation. This is also related to the broader macOS LaunchAgent lifecycle work in #82250, but the root primitive here is specifically launchctl submit for a detached/manual updater job.

Environment

  • OpenClaw: 2026.5.22
  • macOS, Apple Silicon
  • Gateway: macOS LaunchAgent ai.openclaw.gateway
  • Local update wrapper: out-of-band shell wrapper intended to avoid in-band gateway/session shutdown during updates
  • Submitted job family: ai.openclaw.oob-update.*

What happened

A stale self-test updater job created via launchctl submit kept relaunching after successful worker exits:

label = ai.openclaw.oob-update.selftest.20260521T230100Z
state = spawn scheduled / running across checks
runs = 23080+
last exit code = 0
properties = keepalive | inferred program

During the 2026.5.22 update path, the worker correctly observed that the target version was already installed. However, the worker-mode no-op path still continued into plugin sync plus gateway restart. Because the submitted job was being relaunched by launchd after clean exits, that meant repeated gateway restarts even though no package update was needed.

The damaging shape was:

  1. launchctl submit creates a submitted job with inferred keepalive behavior.
  2. Worker exits successfully.
  3. launchd relaunches it anyway.
  4. Worker sees target already installed.
  5. Worker still runs plugin sync plus openclaw gateway restart.
  6. Repeat.

Expected behavior

One-shot macOS updater jobs should run once and stop after success. If the target version is already installed, the worker should exit before plugin sync or gateway restart.

Actual behavior

The submitted job was relaunched after clean exits, and the no-op worker path still reached gateway restart work.

Local fix that validated cleanly

The local wrapper was changed to avoid launchctl submit entirely:

  • generate a per-run LaunchAgent plist
  • RunAtLoad=true
  • KeepAlive=false
  • worker removes its generated plist and boots out its own label on exit
  • worker no-op exits immediately during precheck, before plugin sync or gateway restart
  • gateway restart capped at one per run

Validation run:

runId = 20260524T153034Z
status = noop
gatewayRestartAttempted = false

Post-validation state:

  • generated plist removed
  • no ai.openclaw.oob-update.* jobs left in launchctl list
  • gateway remained healthy on 2026.5.22
  • postflight stayed OK

Suggested upstream fix / docs guidance

If OpenClaw docs, examples, helper scripts, or future update handoff paths mention detached macOS update jobs, avoid launchctl submit for this use case. Prefer a generated one-shot LaunchAgent plist with KeepAlive=false, explicit result/log paths, and self-unload on exit.

It may also be worth broadening stale updater diagnostics beyond only ai.openclaw.update.* labels, or at least documenting that any submitted OpenClaw update job with high runs and last exit code = 0 should be treated as suspicious and booted out.

Related issues

  • #82167: prior manual update launchd job relaunching after clean exit
  • #81859: stale submitted updater job during macOS update
  • #82250: LaunchAgent KeepAlive=true clean-exit restart loop
  • #85120: in-band macOS update can stop the gateway supervising it

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…

FAQ

Expected behavior

One-shot macOS updater jobs should run once and stop after success. If the target version is already installed, the worker should exit before plugin sync or gateway restart.

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 macOS: launchctl submit can relaunch one-shot update jobs after clean exit