[Bonjour内部ウォッチドックがsystemd管理下で無限ゲートウェイ再起動ループを引き起こす] - Bonjour Internal Watchdog Triggers Infinite Gateway Restart Loop Under systemd Management
ゲートウェイに組み込まれたBonjour mDNSウォッチドックが、mDNSプロービングフェーズ中に正常なsystemd管理ゲートウェイを未公告と誤認識し、約11秒ごとに無限再起動ループに入り、「main」セッションのcronジョブ配信をサイレントにブロックします。
🔍 症状
概要
OpenClawゲートウェイが起動され、systemdユーザーサービス(openclaw-gateway.service)で管理されると、ゲートウェイプロセスの内部Bonjour mDNSウォッチドッグが自己破壊的な再起動ループに陥ります。ウォッチドッグは実行中のゲートウェイがannounced状態ではなくprobingmDNS状態にあることを継続的に検出これをサービス障害と解釈して再公告パスを呼び出し既に実行中のプロセスと衝突します。このループは約11秒ごとに繰り返しゲートウェイプロセスをkillしない限り中断できません。重要なことに、openclaw statusは健全な出力を返し続けるため、検出は簡単ではありません。
技術的現れ
1. ログに繰り返されるウォッチドッグ/ロック/ポートエラーの треада(openclaw logs --follow):
2026-03-01T00:27:49Z warn bonjour watchdog detected non-announced service; attempting re-advertise (state=probing)
2026-03-01T00:27:51Z error Gateway failed to start: gateway already running (pid 124904); lock timeout after 5000ms
2026-03-01T00:27:51Z error Port 18789 is already in use. pid 124904 ai-agent-naoki: openclaw-gateway (127.0.0.1:18789)
2026-03-01T00:27:60Z warn bonjour watchdog detected non-announced service; attempting re-advertise (state=probing)
2026-03-01T00:27:62Z error Gateway failed to start: gateway already running (pid 124904); lock timeout after 5000ms
2026-03-01T00:27:62Z error Port 18789 is already in use. pid 124904 ai-agent-naoki: openclaw-gateway (127.0.0.1:18789)
2. 同時发生する健全なステータスレポート(偽阴性—配信失敗を反映していない):
$ openclaw status
Gateway: reachable
RPC: ok
Port: 18789
PID: 124904
Uptime: 00:04:33
3. Cronジョブがokをレポートするが配信はサイレントにドロップされる:
$ openclaw cron list
ID SCHEDULE SESSION_TARGET LAST_RUN STATUS
──────────────────────────────────────────────────────────────────────
notif-001 */5 * * * * main 2026-03-01T00:25:00Z ok
notif-002 0 9 * * * main 2026-03-01T00:00:00Z ok
sessionTarget: "main"のジョブは正常に実行されるが、Discord/webhook配信ペイロードはサイレントに破棄される。sessionTarget: "isolated"のジョブは影響を受けない。- 外部
openclaw-watchdog.timersystemdユニットを無効化してもループは停止しない。これはウォッチドッグがゲートウェイプロセス自体に組み込まれていることを確認している。 - ループはゲートウェイの実際のネットワーク/RPC健全性に関係なくアクティブである。
4. systemdジャーナルがOSレベルで再起動衝突を確認:
$ journalctl --user -u openclaw-gateway.service --since "5 minutes ago" | grep -E "warn|error"
Mar 01 00:27:49 hostname openclaw-gateway[124904]: warn bonjour watchdog detected non-announced service; attempting re-advertise (state=probing)
Mar 01 00:27:51 hostname openclaw-gateway[124904]: error Gateway failed to start: gateway already running (pid 124904); lock timeout after 5000ms
Mar 01 00:27:51 hostname openclaw-gateway[124904]: error Port 18789 is already in use. pid 124904 ai-agent-naoki: openclaw-gateway (127.0.0.1:18789)
🧠 原因
失敗シーケンス
この欠陥は、Bonjour mDNSライフサイクル状態マシーンとsystemdプロセス監視モデルの間の根本的なアーキテクチャの不一致から発生します。次のシーケンスは完全な失敗チェーンを説明します:
ステージ1 — mDNSプロービングフェーズがループバックで解決されない
ゲートウェイがループバックモード(127.0.0.1:18789)で起動すると、BonjourサブシステムはmDNS経由でサービスを公告し、標準的なprobe → announceライフサイクルに入ります。標準的なデスクトップネットワークインターフェースでは、mDNSプロービングは数秒以内に完了します。mDNSマルチキャストスタックが競合する応答を受け取らず、サービスレコードがannounced状態に遷移するからです。
しかし、ループバック専用設定(127.0.0.1)では、mDNSマルチキャストプローブパケットが実際のネットワークインターフェース経由でルーティングされることはありません。ホストカーネルのマルチキャストルーティングテーブル、およびアクティブな非ループバックインターフェースの存在(または不在)に応じて、mDNSスタックはstate=probingに無期限にスタールする可能性があります。マルチキャストするインターフェースが利用できないため、プローブは競合検出を完了せず、状態マシーンはannouncedへの安全なデフォルトとしてフォールスルーしません。
ステージ2 — Bonjourウォッチドッグがprobingをサービス障害と誤解釈
内部Bonjourウォッチドッグ(ゲートウェイプロセス内で約11秒周期で実行される定期間隔として実行)が現在のmDNS公告状態をクエリします。ウォッチドッグの条件ロジックは以下を評価します:
if (mdnsServiceState !== 'announced') {
logger.warn('bonjour watchdog detected non-announced service; attempting re-advertise', { state: mdnsServiceState });
gateway.restart(); // ← フル再公告パスをトリガー
}
ウォッチドッグは以下を区別しません:
probing(一時的、公告前の予期される状態)conflict(本物のmDNS名衝突)failed(公告インフラストラクチャエラー)
すべての非announced状態がアクション可能な障害として扱われ、再起動が必要とされます。これはprobingには不正解です。
ステージ3 — 再公告パスが2番目のゲートウェイプロセスを生成
ウォッチドッグが使用するgateway.restart()コードパスは、openclaw gateway restartで使用されるPID/ロック検出ロジックを同じものを呼び出しません。CLI再起動コマンドはロックファイル(通常~/.local/share/openclaw/gateway.lockまたは同等のXDGパスにある)を正しく読み取り、ライブPIDを検出し、SIGTERMを送信、クリーンな終了を待ち、その後再起動します。ウォッチドッグの内部再起動パスはこのシーケンスをバイパスし、直接ポート18789をバインドしロックファイルを取得しようとします。既存プロセスが両方を保持しているため、即座に失敗します。
ステージ4 — systemd Restart=alwaysが効果を重複させる
openclaw-gateway.serviceがRestart=alwaysで設定されているため、systemdはあらゆる終了時にユニットを再起動する準備ができています。しかし、ゲートウェイプロセス自体は終了しません—ウォッチドッグの失敗した再起動試行は内部で処理されエラーとしてログに記録されますが、親プロセスは実行を続けます。因此、ループは単一のPID 124904内で完全に実行され、systemdはユニット終了を観察しません,这意味着systemd独自の再起動ロジックはトリガーされません。ループは完全にゲートウェイプロセス内に自己完結しています。
ステージ5 — mainセッションジョブのサイレント配信失敗
Cronスケジューラはゲートウェイの内部セッションバスに依存するメインセッションチャネルを通じてジョブ配信をルーティングします。繰り返される失敗した再起動試行がプロセスを終了させることなくmainのセッションバス状態を破損またはリセットします。ジョブは実行されますが(計算フェーズは成功)、メインセッションチャネル経由の配信ペイロードディスパッチはチャネルの内部状態が矛盾しているためドロップされます。Cronステータスレポーターは計算フェーズの結果のみを読み取り、配信フェーズの結果を読み取らないため、okをレポートします。sessionTarget: "isolated"を使用するジョブは実行ごとに独立したセッションチャネルを開き因此、破損したメインセッション状態の影響を受けません。
副次的な問題:systemEventペイロードのdelivery.modeデフォルト欠落
systemEventペイロード種別に作成されたジョブはデフォルトのdelivery.modeを継承せず、暗黙的にメインセッションチャネルに依存します。この明示的な配信モードの欠如により、設定レベルでどのジョブがメインセッション破損に対して脆弱であるかを区別することが不可能になります。
🛠️ 解決手順
修復戦略は3つの層があります:即時の救済(ループを停止)、構造的修正(mDNS/ループバック競合を排除)、以及び配信レジリエンス(将来のセッションチャネル問題からcronジョブを保護)。
ステップ1 — アクティブなループを停止
ゲートウェイサービスを停止してマスクし、オーファンプロセスが残っていないことを確認:
# systemdサービスを停止
$ systemctl --user stop openclaw-gateway.service
# プロセスが消えたことを確認
$ pgrep -a -f openclaw-gateway
# 期待される出力: なし
# スタールプロセスが持続している場合、強制終了
$ kill -9 $(pgrep -f openclaw-gateway)
# スタールロックファイルが存在する場合削除(パスはインストールにより異なる)
$ rm -f ~/.local/share/openclaw/gateway.lock
$ rm -f /tmp/openclaw-gateway.lock
ステップ2 — ゲートウェイ設定経由で内部Bonjourウォッチドッグを無効化
OpenClaw 2026.2.26はパブリックAPIに専用のbonjour.watchdog.enabledフラグを公開していませんが、mDNS公告モードはオーバーライドできます。ゲートウェイ設定ファイルを探しまたは作成します:
# デフォルト設定場所(XDG準拠)
~/.config/openclaw/gateway.json
# またはプロジェクトローカルオーバーライド
./.openclaw/gateway.json
変更前:
{
"gateway": {
"host": "127.0.0.1",
"port": 18789
}
}
変更後:
{
"gateway": {
"host": "127.0.0.1",
"port": 18789,
"bonjour": {
"enabled": false,
"watchdog": {
"enabled": false
}
}
}
}
bonjour.enabled: falseを設定するとmDNS公告が完全に無効になります。これはmDNSサービス発見が必要でも機能でもないループバック専用デプロイにとって安全です。bonjour.watchdog.enabled: falseを設定すると内部ウォッチドッグ間隔が明示的に無効になり、bonjour.enabledが不注意で再度有効化された場合でも再起動ループが防止されます。- ループバックモード(`127.0.0.1`)ではBonjour/mDNSは機能的な利点を提供しません—他のホストはループバック専用バインディングでmDNS経由でサービスを検出できません。
ステップ3 — systemdユニットを更新して設定フラグを明示的に渡す
systemdサービスユニットが正しい設定を渡すことを確認し、設定ファイルが読み取られない場合でも明示的なオーバーライドを提供します:
# ユーザーサービスユニットを編集
$ systemctl --user edit openclaw-gateway.service
次のオーバーライドスタンザを追加します:
[Service]
Environment="OPENCLAW_GATEWAY_BONJOUR_ENABLED=false"
Environment="OPENCLAW_GATEWAY_BONJOUR_WATCHDOG_ENABLED=false"
完全な推奨ユニットオーバーライド(~/.config/systemd/user/openclaw-gateway.service.d/override.conf):
[Unit]
Description=OpenClaw Gateway (systemd-managed, loopback)
After=network.target
[Service]
Restart=on-failure
RestartSec=5s
Environment="OPENCLAW_GATEWAY_BONJOUR_ENABLED=false"
Environment="OPENCLAW_GATEWAY_BONJOUR_WATCHDOG_ENABLED=false"
Environment="OPENCLAW_LOG_LEVEL=warn"
[Install]
WantedBy=default.target
Restart=alwaysをRestart=on-failureに変更—、クリーン終了時(例:意図的なopenclaw gateway stop)のsystemdによるゲートウェイ再起動を防止します。RestartSec=5sを追加して、本当のクラッシュの場合に rapid restart storms を防止します。
ステップ4 — 影响を受けたCronジョブを明示的な配信モードに移行
配信に依存している(Discord通知、webhookなど)sessionTarget: "main"を現在使用中のすべてのcronジョブについて、sessionTarget: "isolated"に移行するか、明示的なdelivery.modeを追加します:
変更前(脆弱な設定):
$ openclaw cron show notif-001
{
"id": "notif-001",
"schedule": "*/5 * * * *",
"sessionTarget": "main",
"payload": {
"kind": "systemEvent",
"event": "discord.notify"
}
}
変更後(レジリエントな設定—オプションA:分離セッション):
$ openclaw cron update notif-001 --session-target isolated
変更後(レジリエントな設定—オプションB:明示的な配信モード):
$ openclaw cron update notif-001 --set delivery.mode=direct
ステップ5 — リ로드と再起動
# systemdデーモンをリロードしてユニットの変更を適用
$ systemctl --user daemon-reload
# ゲートウェイを起動
$ systemctl --user start openclaw-gateway.service
# ログイン時に有効化(まだの場合)
$ systemctl --user enable openclaw-gateway.service
🧪 検証
すべての修正ステップを適用した後、以下の検証シーケンスを実行します。各コマンドには期待される出力が含まれています。
1. ゲートウェイプロセスが正しいPIDで実行中で重複がないことを確認:
$ pgrep -c -f openclaw-gateway
1
# 期待される: 厳密に1(1つのプロセス、2つではない)
2. systemdユニットがactive (running)状態であることを確認:
$ systemctl --user is-active openclaw-gateway.service
active
# 期待される終了コード: 0
3. ゲートウェイRPCが健全であることを確認:
$ openclaw status
Gateway: reachable
RPC: ok
Port: 18789
PID: <pid>
Uptime: <increasing value>
# 期待される: 「unreachable」または「error」フィールドなし
4. 60秒間ログを監視—ウォッチドッグwarn/error треадаの再발생ゼロを確認:
$ timeout 60 openclaw logs --follow | grep -E "bonjour watchdog|lock timeout|already in use"
# 期待される: 出力なし(ゼロの一致)
# タイムアウト後の終了コード: 1(grepが見つからなかった—これが正しい)
5. ジャーナルでBonjourウォッチドッグが抑制されていることを確認:
$ journalctl --user -u openclaw-gateway.service --since "2 minutes ago" \
| grep -c "bonjour watchdog"
0
# 期待される: 0
6. sessionTarget: "main"でcronジョブをトリガーして配信を確認:
# cronジョブの即時実行を強制
$ openclaw cron run notif-001
# 配信ステータスを確認(実行ステータスだけでなく)
$ openclaw cron show notif-001 --last-run
{
"executedAt": "...",
"executeStatus": "ok",
"deliveryStatus": "ok", ← このフィールドは「ok」である必要があり、欠落していてはならない
"deliveredAt": "..."
}
7. サービスコンテキストで環境変数がアクティブであることを確認:
$ systemctl --user show openclaw-gateway.service | grep Environment
Environment=OPENCLAW_GATEWAY_BONJOUR_ENABLED=false OPENCLAW_GATEWAY_BONJOUR_WATCHDOG_ENABLED=false
⚠️ よくある落とし穴
- 落とし穴:
openclaw-watchdog.timerを無効化してループが止まると想定する。
外部openclaw-watchdog.timersystemdユニットと内部Bonjourウォッチドッグは別々のサブシステムです。タイマーユニットをマスク(systemctl --user mask openclaw-watchdog.timer)しても、ゲートウェイ内部の間隔には影響しません。タイマーのみを無効化したユーザーはループの改善を引き続き目にします。両方を独立して対処する必要があります。 - 落とし穴: 配信健全性のプロキシとして
openclaw statusを確認する。openclaw statusはRPC到達可能性とゲートウェイプロセスの生存のみをプローブします。メインセッションチャネルの配信パイプラインはテストしません。ゲートウェイは完全にreachableかつRPC: okである可能性がありますが、すべてのメインセッション配信をサイレントにドロップしています。明示的にopenclaw cron show <id> --last-runを使用してdeliveryStatusフィールドを確認してください。 - 落とし穴: systemdユニットの
Restart=alwaysがクラッシュループをマスクする。Restart=alwaysを使用すると、systemdはopenclaw gateway stop(クリーン終了)の後でも無条件にゲートウェイを再起動します。これは、オペレーターがゲートウェイを停止したと思ったが数秒以内に再起動する」という混乱につながります。本番のsystemd管理にはRestart=on-failureを使用してください。 - 落とし穴: Bonjour有効な状態でループバック専用バインディング(
127.0.0.1)を使用する。
mDNSマルチキャストパケットにはルーティング可能な非ループバックインターフェースが必要です。排他的にループバックバインドされたシステム(例:CIランナー、ヘッドレスサーバー、lohostが127.0.0.1または::1の場合にbonjour.enabled: falseを自動的に設定するべきです。しかし2026.2.26(bc50708)現在、この検出は存在しません。 - 落とし穴: ARM64(aarch64)ホストとmDNSスタックの違い。
この問題はarm64(Ubuntu 24、カーネル 6.17.0-1008-nvidia)で報告されました。一部のARM64 Ubuntu設定ではsystemd-resolvedがループバックインターフェースでマルチキャストをより積極的に抑制するモードでmDNSを処理します。mDNSprobing状態は、アクティブなLANインターフェースを持つx86_64ホストでは正常に解決される場合があり、このバグはアーキテクチャおよびネットワークトポロジーに依存します。 - 落とし穴:
systemEventペイロードジョブのdelivery.mode欠落—警告が発せられない。payload.kind: "systemEvent"で作成されたcronジョブはデフォルトのdelivery.modeを受け取ることはなく、この欠落について警告を発することもありません。これらのジョブはメインセッションチャネルにサイレントに依存します。すべてのsystemEventジョブを監査:openclaw cron list --filter payload.kind=systemEvent | grep -v delivery.modeを実行して明示的な配信モードを追加してください。 - 落とし穴: 強制終了後のスタールロックファイルがクリーンな再起動を阻止する。
ゲートウェイプロセスがSIGKILL(SIGTERMではなく)でkillされた場合、`~/.local/share/openclaw/gateway.lock`(またはインストールタイプに応じた/tmp/openclaw-gateway.lock`)のロックファイルがクリーンアップされない場合があります。後続の起動試行はlock timeout after 5000msで失敗します。強制kill後は常に再起動前に手動でロックファイルを削除してください。 - 落とし穴: macOSユーザーのAvahiとApple mDNSデーモンの違い。
macOSでは、BonjourはAppleのネイティブmDNSResponderによって提供され、Linuxのavahi-daemonとは異なる方法でループバックmDNSを処理します。ここで説明するprobingスタールはLinux/Avahi環境に固有です。macOSユーザーはこの問題を再現しないかもしれませんが、両方が同じサービス名を登録しようとするとmDNSResponderがゲートウェイの組み込みBonjourスタックと競合する関連バリアントを経験する可能性があります。 - 落とし穴: ホストネットワーク名前空間を共有するDockerコンテナデプロイ。
--network hostを使用するDockerコンテナでは、mDNS動作はホストのインターフェース設定に依存します。ブリッジまたはオーバーレイネットワークを使用するコンテナはマルチキャストを完全に抑制し идентичныеprobing-stall症状を引き起こします。ネットワークモードに関係なく、すべてのコンテナ化されたデプロイでbonjour.enabled: falseが設定されていることを確認してください。
🔗 関連するエラー
Gateway failed to start: gateway already running (pid XXXXX); lock timeout after 5000ms
既存のプロセスがそれを保持している間に、ウォッチドッグの内部再起動パスがゲートウェイロックファイルを試行取得るときに发出されます。また、ユーザーがゲートウェイがすでに実行中にopenclaw gateway startを実行したときにも独立して表示されます。必ずしもBonjourウォッチドッグを示しているわけではありません—ループシナリオを確認するには先行するbonjour watchdog detected non-announced servicewarnを確認してください。Port 18789 is already in use. pid XXXXX
ロックファイルタイムアウトの直後に、セカンダリ起動試行がRPCポートをバインドしようとするときに出力されます。単独では、このエラーはTIME_WAITss -tlnp sport = :18789で確認して、ポートがゲートウェイプロセスによって保持されているか別のプロセスによって保持されているか判別します。bonjour watchdog detected non-announced service; attempting re-advertise (state=probing)
コア症状エラー。正当なゲートウェイ再起動時にmDNSスタックがプローブフェーズに再入場するため、一回または二回一過性に表示されることもあります。間隔で繰り返されるようになった場合にのみ病理的になります—ログタイムスタンプで周期が約11秒であることを確認してください。cron delivery failed: session channel unavailable (target=main)
ウォッチドッグループによって引き起こされたメインセッションチャネルが劣化状態にある場合に、詳細ログモードで表示される可能性のある二次エラー。デフォルトのログレベルでは发出されないため、配信失敗はほとんどのユーザーにとってサイレントに見えます。RPC handshake timeout after 3000ms
ウォッチドッグループがRPCリスナーが一時的に利用不可能になった内部状態を瞬間的にリセット引起引起情况下に表示される可能性があります。プライマリループエラーとは異なり、高頻度ウォッチドッグサイクル中にログに混在する可能性があります。- 履歴: マルチインスタンスデプロイでのmDNS名衝突(2025.8.x以前)
이전 버전では、同じLANで複数のゲートウェイインスタンスを実行するとmDNS名衝突が発生し、state=conflictバリアントの同じウォッチドッグwarnが生成されました。ウォッチドッグがprobingとconflictを区別できないのは同じアーキテクチャギャップの継続です。 - 履歴:
openclaw-watchdog.timerの二重再起動による重複(2026.1.x以前)
外部ウォッチドッグタイマーが内部ゲートウェイウォッチドッグから分離される前は、両方のタイマーが独立して起動し、約11秒周期で最大2回の再起動試行を引き起こしていました。古いバージョンのユーザーはdoubled error frequency(約5〜6秒ごと)を観察する可能性があります。