Skip to content

macOS LaunchAgent KeepAlive=true restarts after clean already-running gateway exit #82250

Description

@amzzzzzzz

Summary

On macOS with OpenClaw 2026.5.12, the generated LaunchAgent uses KeepAlive=true. If a foreground/manual gateway already owns the configured port, starting the LaunchAgent causes the service process to detect the existing healthy gateway, exit cleanly with code 0, and then launchd restarts it again. This can create repeated gateway startup/shutdown churn even though there is no config or runtime crash.

This looks like the macOS equivalent of the supervisor restart-cascade behavior discussed in #80696, and the local workaround matches the proposed direction in #73849.

Environment

  • OpenClaw: 2026.5.12 (f066dd2)
  • macOS: arm64, macOS 26.x
  • Node: 22.22.0
  • Install/runtime: npm global install, macOS LaunchAgent
  • Gateway port: 18789

What happened

  1. A foreground/manual gateway was running with:

    openclaw gateway run
    
  2. The LaunchAgent was installed with:

    KeepAlive = true
    RunAtLoad = true
  3. Running openclaw gateway start loaded the LaunchAgent.

  4. The LaunchAgent process started, noticed the existing gateway on 18789, and exited cleanly. Logs showed repeated entries like:

    [gateway] starting HTTP server...
    [health-monitor] started
    [shutdown] started: gateway startup failed
    [shutdown] completed cleanly
    [gateway] already running under launchd; existing gateway is healthy, leaving it in control
    
  5. Because KeepAlive=true restarts after any exit, including successful exit, launchd kept starting another gateway process roughly every throttle interval.

This produced a loop of launchd-owned gateway attempts while the foreground gateway continued to own the port.

Clean local test

I patched the local LaunchAgent plist from:

KeepAlive = true

to:

KeepAlive = {
  SuccessfulExit = false
}

Then I reran openclaw gateway start while the foreground gateway still owned 18789.

Observed result after 30 seconds:

state = not running
runs = 1
last exit code = 0
semaphores = {
  successful exit => 0
}

The foreground gateway remained healthy, and the LaunchAgent did not keep restarting.

Expected behavior

If the gateway process exits cleanly because it detected an already-running healthy gateway, launchd should not keep restarting it forever.

Actual behavior

With KeepAlive=true, launchd restarts after clean exit code 0, creating avoidable churn and making recovery/handoff/debugging much harder.

Suggested fix

Generate the macOS LaunchAgent with:

KeepAlive = {
  SuccessfulExit = false
}

instead of KeepAlive=true, as proposed in #73849.

Related issues/PRs:

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High-priority user-facing bug, regression, or broken workflow.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:crash-loopCrash, hang, restart loop, or process-level availability failure.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.maturity:stableIssue affects a taxonomy feature currently scored M4/M5.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions