Skip to content

macOS: avoid launchd startup crash loops#81726

Closed
BunsDev wants to merge 1 commit into
mainfrom
meow/macos-launchd-crash-policy
Closed

macOS: avoid launchd startup crash loops#81726
BunsDev wants to merge 1 commit into
mainfrom
meow/macos-launchd-crash-policy

Conversation

@BunsDev

@BunsDev BunsDev commented May 14, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Change generated macOS LaunchAgents to relaunch clean exits via KeepAlive.SuccessfulExit=true instead of respawning every non-zero startup failure.
  • Preserve current ExitTimeOut, ProcessType, ThrottleInterval, and Umask launchd hardening.
  • Teach service audit to accept the new canonical launchd KeepAlive policy.

Fixes #73673.

Verification

Behavior addressed: non-zero launchd startup failures stay stopped for inspection while clean-exit update/restart handoffs still relaunch.
Real environment tested: local Codex worktree on macOS with targeted unit/docs proof; no live launchd smoke yet.
Exact steps or command run after this patch: PATH=/Users/buns/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin:$PATH node scripts/run-vitest.mjs src/daemon/launchd.test.ts src/daemon/service-audit.test.ts; PATH=/Users/buns/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin:$PATH pnpm exec oxfmt --check --threads=1 src/daemon/launchd-plist.ts src/daemon/launchd.test.ts src/daemon/service-audit.ts src/daemon/service-audit.test.ts; PATH=/Users/buns/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin:$PATH pnpm docs:list; git diff --check.
Evidence after fix: targeted Vitest passed 2 files, 86 tests passed; oxfmt check passed; docs:list completed; git diff whitespace check passed.
Observed result after fix: generated launchd plist contains KeepAlive dict with SuccessfulExit=true and still includes shutdown/process/throttle policy; service audit accepts that canonical policy and rejects missing KeepAlive.
What was not tested: live macOS LaunchAgent crash-loop smoke and broad Testbox pnpm check:changed are still pending for the full four-slice queue.

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation app: macos App: macos gateway Gateway runtime size: S maintainer Maintainer-authored PR labels May 14, 2026
@clawsweeper

clawsweeper Bot commented May 14, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs changes before merge.

Summary
The PR changes macOS LaunchAgent generation, service audit, docs, tests, and changelog to use KeepAlive.SuccessfulExit=true for clean-exit relaunches while leaving non-zero startup failures stopped.

Reproducibility: yes. source-backed rather than live: current main writes boolean KeepAlive=true, launchd docs say that keeps the job alive unconditionally, and the linked issue reports the resulting startup-failure restart loop on macOS 15.4.

Real behavior proof
Not applicable: The author is a MEMBER and the PR carries the maintainer label, so the external contributor proof gate does not apply; the body still notes live launchd smoke is pending before landing confidence.

Next step before merge
There is one narrow, file-local blocker in the service-audit policy and regression coverage that automation can repair on this PR branch.

Security
Cleared: The diff changes launchd plist policy, audit matching, docs, tests, and changelog without adding dependencies, permissions, secret handling, downloaded artifacts, or new code execution sources.

Review findings

  • [P2] Flag legacy KeepAlive=true as noncanonical — src/daemon/service-audit.ts:190
Review details

Best possible solution:

Make service audit/doctor treat boolean KeepAlive=true as stale or noncanonical, keep the generated SuccessfulExit=true policy, then run targeted tests plus live macOS launchd smoke before landing and resolving the linked issue/older PR.

Do we have a high-confidence way to reproduce the issue?

Yes, source-backed rather than live: current main writes boolean KeepAlive=true, launchd docs say that keeps the job alive unconditionally, and the linked issue reports the resulting startup-failure restart loop on macOS 15.4.

Is this the best way to solve the issue?

No, not as currently patched. SuccessfulExit=true is a maintainable generator policy, but the audit path should not keep accepting legacy KeepAlive=true plists as healthy if existing installs need to move off the crash-loop policy.

Full review comments:

  • [P2] Flag legacy KeepAlive=true as noncanonical — src/daemon/service-audit.ts:190
    This still treats existing LaunchAgents with <key>KeepAlive</key><true/> as healthy. Those plists are the crash-looping policy this PR replaces, so doctor/status audit will not recommend rewriting an already-installed agent and affected users can keep looping after upgrading. Drop the boolean-true branch or report it as a repair issue, and add a regression test with the old plist.
    Confidence: 0.87

Overall correctness: patch is incorrect
Overall confidence: 0.86

Acceptance criteria:

  • node scripts/run-vitest.mjs src/daemon/service-audit.test.ts src/daemon/launchd.test.ts
  • pnpm exec oxfmt --check --threads=1 src/daemon/service-audit.ts src/daemon/service-audit.test.ts src/daemon/launchd-plist.ts src/daemon/launchd.test.ts
  • git diff --check
  • Before merge, run a live macOS LaunchAgent smoke or equivalent Crabbox/Testbox proof for non-zero startup failure staying stopped and clean-exit restart handoff relaunching.

What I checked:

  • current-main launchd policy: Current main renders generated LaunchAgents with boolean KeepAlive=true, plus RunAtLoad, ExitTimeOut, ProcessType, ThrottleInterval, and Umask. (src/daemon/launchd-plist.ts:183, 11017c93cf14)
  • current-main audit policy: Current main's audit accepts only boolean KeepAlive=true as the launchd keepalive policy before this PR. (src/daemon/service-audit.ts:189, 11017c93cf14)
  • PR diff: The PR changes generated plists to <key>KeepAlive</key><dict><key>SuccessfulExit</key><true/></dict> and changes audit to accept either that dictionary or the legacy boolean form. (src/daemon/service-audit.ts:190, 68bf98c40700)
  • launchd contract: The launchd.plist man page states SuccessfulExit=true restarts a job when it exits with status zero, while KeepAlive=true unconditionally keeps the job alive.
  • related issue context: The linked issue remains open and prior ClawSweeper review kept it open because current main still emits boolean KeepAlive=true; it also noted older fix(daemon): use KeepAlive SuccessfulExit=false to prevent launchd crash-loop #73849 as an open conflicting attempt rather than a merged fix.
  • history provenance: Line blame ties the current launchd plist and audit implementation in this checkout to commit c04bbd3, while GitHub commit metadata shows recent adjacent launchd lifecycle work by BunsDev and vincentkoc. (src/daemon/launchd-plist.ts:183, c04bbd3cbb9d)

Likely related people:

  • BunsDev: Authored recent merged macOS update restart lifecycle hardening that touched src/daemon/launchd-plist.ts, src/daemon/launchd.test.ts, and gateway restart-loop behavior before this PR. (role: recent area contributor; confidence: high; commits: fa79e9754ecc; files: src/daemon/launchd-plist.ts, src/daemon/launchd.test.ts, src/cli/gateway-cli/run-loop.ts)
  • vincentkoc: Local blame points current launchd/audit lines at Vincent Koc in this checkout, and recent merged work hardened the launchd restart handoff path. (role: recent adjacent contributor; confidence: medium; commits: c04bbd3cbb9d, 57f5b3b201b4; files: src/daemon/launchd-plist.ts, src/daemon/service-audit.ts, src/daemon/launchd-restart-handoff.ts)
  • steipete: Sampled shortlog shows the heaviest contribution count across the central launchd/audit files, and recent history includes restart-loop changelog/landing coordination around LaunchAgent KeepAlive semantics. (role: area steward; confidence: medium; commits: 7f863e22b06f; files: src/daemon/launchd.ts, src/daemon/launchd-plist.ts, src/daemon/service-audit.ts)

Remaining risk / open question:

Codex review notes: model gpt-5.5, reasoning high; reviewed against 11017c93cf14.

Re-review progress:

@BunsDev

BunsDev commented May 14, 2026

Copy link
Copy Markdown
Contributor Author

Broad verification update:

  • Testbox pnpm check:changed passed for meow/macos-launchd-crash-policy.
  • Testbox: tbx_01krjq5vz4tv5aekay8332hcj4
  • Command: (git fetch/unshallow origin main for merge-base) && pnpm check:changed
  • Result: exit 0.

@BunsDev

BunsDev commented Jun 13, 2026

Copy link
Copy Markdown
Contributor Author

Closing this branch, but not dropping the problem. #73673 is still a real macOS launchd issue; this PR is just too stale to be the safe way to land it now. I’m going to treat the right next step as a fresh, narrow PR against current main that only addresses the crash-loop policy, with updated launchd/service-audit tests and no carried-over stale changelog noise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: macos App: macos docs Improvements or additions to documentation gateway Gateway runtime maintainer Maintainer-authored PR size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Gateway repeatedly restarts under launchd after crash, making it difficult to debug

1 participant