Skip to content

OpenClaw macOS LaunchAgent 重启后 bootstrap 未执行导致 Gateway 永久停止

问题

在 macOS 上通过 LaunchAgent(plist)管理 OpenClaw Gateway 时,update.run 触发内部重启后,launchctl bootout 执行成功但 launchctl bootstrap 从未运行,Gateway 进程永久消失:

[gui/501/ai.openclaw.gateway]: removing service: ai.openclaw.gateway

即使配置了 KeepAlive: true,Gateway 也不会自动恢复——因为 bootout 之后服务已从 launchd 注销,launchd 对它毫不知情。

典型场景:凌晨 4:00 openclaw update 自动更新成功,早上 11:30 Gateway 内部触发一次重启,bootout 成功、bootstrap 失败,Gateway 死了 2 小时多直到手动干预。

根本原因:OpenClaw 的 update/restart 流程执行 launchctl bootoutlaunchctl bootstrap 之间存在竞态窗口——若子进程链意外断开(node → zsh → node 的调用链中任何一环退出),bootstrap 步骤被跳过,没有恢复机制。ThrottleInterval: 1 过小也可能导致 launchd 认为服务在短时间内异常退出并拒绝立即重新注册。

解决方案

方案一:增大 ThrottleInterval(防止 launchd 拒绝快速重注册)

~/Library/LaunchAgents/ai.openclaw.gateway.plist 中调整:

xml
<key>ThrottleInterval</key>
<integer>10</integer>

调整后重新加载 plist:

bash
launchctl unload ~/Library/LaunchAgents/ai.openclaw.gateway.plist
launchctl load ~/Library/LaunchAgents/ai.openclaw.gateway.plist

方案二:添加看门狗脚本(补偿 bootstrap 缺失)

创建 ~/Library/LaunchAgents/ai.openclaw.watchdog.plist

xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>ai.openclaw.watchdog</string>
  <key>ProgramArguments</key>
  <array>
    <string>/bin/bash</string>
    <string>-c</string>
    <string>
      if ! launchctl list | grep -q ai.openclaw.gateway; then
        launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.openclaw.gateway.plist
      fi
    </string>
  </array>
  <key>StartInterval</key>
  <integer>60</integer>
</dict>
</plist>
bash
launchctl load ~/Library/LaunchAgents/ai.openclaw.watchdog.plist

每分钟检测一次 Gateway 是否还注册在 launchd 中,丢失则自动 bootstrap。

方案三:紧急恢复命令

若 Gateway 已消失,立即执行:

bash
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.openclaw.gateway.plist

诊断确认

验证 Gateway 是否还在 launchd 注册:

bash
launchctl list | grep openclaw
# 有输出 = 已注册;无输出 = 需要手动 bootstrap

常见问题

Q: KeepAlive: true 为什么没有帮助?

A: KeepAlive 依赖 launchd 知道该服务存在。bootout 成功执行后,服务从 launchd 的注册表中彻底删除,launchd 无法"保持存活"一个它不认识的服务。

Q: 这个问题在 Linux(systemd)上也会发生吗?

A: 不会。systemd 的 Restart=always 即使在 stop 后重新注册也有完善的事务保障;macOS launchd 的 bootout/bootstrap 两步操作没有原子性保证,是 macOS 特有问题。

Q: 升级到最新 OpenClaw 版本能否修复?

A: 关注 GitHub #40089,官方尚无修复。建议在修复发布前部署方案二(看门狗脚本)作为长期防护。