Skip to content

macOS: launchctl submit can relaunch one-shot update jobs after clean exit #86104

Description

@amzzzzzzz

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High-priority user-facing bug, regression, or broken workflow.impact:crash-loopCrash, hang, restart loop, or process-level availability failure.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions