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:
launchctl submit creates a submitted job with inferred keepalive behavior.
- Worker exits successfully.
- launchd relaunches it anyway.
- Worker sees target already installed.
- Worker still runs plugin sync plus
openclaw gateway restart.
- 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
Summary
On macOS,
launchctl submitis 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 oropenclaw gateway restartafter 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.22update wrapper validation. This is also related to the broader macOS LaunchAgent lifecycle work in #82250, but the root primitive here is specificallylaunchctl submitfor a detached/manual updater job.Environment
2026.5.22ai.openclaw.gatewayai.openclaw.oob-update.*What happened
A stale self-test updater job created via
launchctl submitkept relaunching after successful worker exits:During the
2026.5.22update 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:
launchctl submitcreates a submitted job with inferred keepalive behavior.openclaw gateway restart.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 submitentirely:RunAtLoad=trueKeepAlive=falseValidation run:
Post-validation state:
ai.openclaw.oob-update.*jobs left inlaunchctl list2026.5.22Suggested upstream fix / docs guidance
If OpenClaw docs, examples, helper scripts, or future update handoff paths mention detached macOS update jobs, avoid
launchctl submitfor this use case. Prefer a generated one-shot LaunchAgent plist withKeepAlive=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 highrunsandlast exit code = 0should be treated as suspicious and booted out.Related issues
KeepAlive=trueclean-exit restart loopopenclaw updateon macOS LaunchAgent can stop the gateway supervising it #85120: in-band macOS update can stop the gateway supervising it