Skip to content

[Bug]: gateway crash loop on 2026.4.24 (bonjour plugin) #71769

@jplavoiemtl

Description

@jplavoiemtl

Bug type

Regression (worked before, now fails)

Beta release blocker

No

Summary

Bug report (filed upstream)

Summary

On 2026.4.24, the gateway enters an unrecoverable crash loop on systems
where the bonjour service stays in `announcing` state. The bonjour plugin
defines a handler specifically to swallow ciao cancellation rejections, but
the plugin entry point never passes the runtime's
`registerUnhandledRejectionHandler` into deps, so the handler is never
wired to the process. The watchdog's own `recreateAdvertiser` then
triggers `CIAO ANNOUNCEMENT CANCELLED`, which goes unhandled and exits the
process with status 1. systemd restarts and the cycle repeats forever.

Environment

  • OpenClaw 2026.4.24 (cbcfdf6)
  • Node 22.22.2, linux/arm64 (Arduino Uno Q, "watt-uno-q")
  • Was previously running 2026.4.23 with no issue

Symptoms

Every 3–4 minutes the gateway exits and is restarted by systemd:

```
[plugins] bonjour: advertised gateway ... state=announcing
[plugins] bonjour: restarting advertiser (service stuck in announcing for 112269ms ...)
[openclaw] Unhandled promise rejection: CIAO ANNOUNCEMENT CANCELLED
[openclaw] wrote stability bundle: .../openclaw-stability-...-unhandled_rejection.json
openclaw-gateway.service: Main process exited, code=exited, status=1/FAILURE
```

`openclaw gateway status` reports the service as running but the
connectivity probe to `ws://127.0.0.1:18789` returns `ECONNREFUSED`
because the process is in the middle of restarting.

Root cause

In `dist/extensions/bonjour/index.js`:

The plugin defines `handleCiaoUnhandledRejection` (lines ~102–111) which
is explicitly written to classify and ignore `CIAO
ANNOUNCEMENT|PROBING CANCELLED` rejections via
`CIAO_CANCELLATION_MESSAGE_RE`.

is explicitly written to classify and ignore `CIAO
ANNOUNCEMENT|PROBING CANCELLED` rejections via
`CIAO_CANCELLATION_MESSAGE_RE`.

          { logger: api.logger }                  // <-- no handler dep passed
      )).stop };
  }

So the rejection handler is dead code. By contrast, extensions/telegram/probe-*.js
and extensions/whatsapp/monitor-*.js both call registerUnhandledRejectionHandler
correctly via the plugin SDK, and don't crash on similar transient rejections.

How the crash gets triggered

  1. svc.advertise() is called; service enters announcing state.
  2. mDNS replies don't come back within the watchdog's stuck threshold
    (STUCK_ANNOUNCING_MS), so recreateAdvertiser(...) runs.
  3. stopCycle(cycle) cancels the in-flight ciao announcement, which rejects
    with CIAO ANNOUNCEMENT CANCELLED.
  4. No process-level handler is registered (see above) → unhandled rejection
    → process exits 1.

Suggested fix

Pass the handler registry from the plugin SDK into deps at the call site, e.g.:

  import { registerUnhandledRejectionHandler } from "openclaw/plugin-sdk/runtime-env";
  ...
  advertise: async (ctx) => {
      return { stop: (await startGatewayBonjourAdvertiser(
          { ...opts },
          { logger: api.logger, registerUnhandledRejectionHandler }
      )).stop };
  }

(Or expose it via api.* if that's the preferred plugin convention.)

After this fix the gateway should remain stable even when mDNS is flaky — the
watchdog will silently restart the advertiser instead of crashing the gateway.

Workaround

Setting OPENCLAW_DISABLE_BONJOUR=1 in the systemd unit makes
startGatewayBonjourAdvertiser return a no-op (line ~94, isDisabledByEnv()),
which fully resolves the crash loop. LAN auto-discovery via openclaw.local is
lost; loopback / IP-based access is unaffected.

Stability bundle

Attached: ~/.openclaw/logs/stability/openclaw-stability-*-unhandled_rejection.json
(content is sparse — only metadata, no stack — likely because the stability
recorder never captured events for the rejection itself.)

Steps to reproduce

Gateway does not load properly (bonjour plugin)

Expected behavior

Gateway crash loop on 2026.4.24: bonjour plugin's ciao rejection handler is never registered

Summary

On 2026.4.24, the gateway enters an unrecoverable crash loop on systems where the
bonjour service stays in announcing state. The bonjour plugin defines a handler
specifically to swallow ciao cancellation rejections, but the plugin entry point
never passes the runtime's registerUnhandledRejectionHandler into deps, so the
handler is never wired to the process. The watchdog's own recreateAdvertiser
then triggers CIAO ANNOUNCEMENT CANCELLED, which goes unhandled and exits the
process with status 1. systemd restarts and the cycle repeats forever.

Version

  • OpenClaw 2026.4.24 (cbcfdf6)
  • Node 22.22.2, linux/arm64 (Arduino Uno Q, "watt-uno-q")
  • Was previously running 2026.4.14 with no issue

Symptoms

Every 3–4 minutes the gateway exits and is restarted by systemd:

  [plugins] bonjour: advertised gateway ... state=announcing
  [plugins] bonjour: restarting advertiser (service stuck in announcing for 112269ms ...)
  [openclaw] Unhandled promise rejection: CIAO ANNOUNCEMENT CANCELLED
  [openclaw] wrote stability bundle: .../openclaw-stability-...-unhandled_rejection.json
  openclaw-gateway.service: Main process exited, code=exited, status=1/FAILURE

openclaw gateway status reports the service as running but the connectivity
probe to ws://127.0.0.1:18789 returns ECONNREFUSED because the process is in the
middle of restarting.

Actual behavior

Gateway crash loop on 2026.4.24: bonjour plugin's ciao rejection handler is never registered

Summary

On 2026.4.24, the gateway enters an unrecoverable crash loop on systems where the
bonjour service stays in announcing state. The bonjour plugin defines a handler
specifically to swallow ciao cancellation rejections, but the plugin entry point
never passes the runtime's registerUnhandledRejectionHandler into deps, so the
handler is never wired to the process. The watchdog's own recreateAdvertiser
then triggers CIAO ANNOUNCEMENT CANCELLED, which goes unhandled and exits the
process with status 1. systemd restarts and the cycle repeats forever.

Version

  • OpenClaw 2026.4.24 (cbcfdf6)
  • Node 22.22.2, linux/arm64 (Arduino Uno Q, "watt-uno-q")
  • Was previously running 2026.4.14 with no issue

Symptoms

Every 3–4 minutes the gateway exits and is restarted by systemd:

  [plugins] bonjour: advertised gateway ... state=announcing
  [plugins] bonjour: restarting advertiser (service stuck in announcing for 112269ms ...)
  [openclaw] Unhandled promise rejection: CIAO ANNOUNCEMENT CANCELLED
  [openclaw] wrote stability bundle: .../openclaw-stability-...-unhandled_rejection.json
  openclaw-gateway.service: Main process exited, code=exited, status=1/FAILURE

openclaw gateway status reports the service as running but the connectivity
probe to ws://127.0.0.1:18789 returns ECONNREFUSED because the process is in the
middle of restarting.

OpenClaw version

2026.4.24

Operating system

Ubuntu

Install method

No response

Model

GPT-5.4

Provider / routing chain

Codex OpenAI

Additional provider/model setup details

No response

Logs, screenshots, and evidence

Impact and severity

No response

Additional information

Bug: gateway crash loop on 2026.4.24 (bonjour plugin)
Status: active — workaround applied locally, upstream fix pending Reported by: JP (photojpl360@gmail.com) Date filed: 2026-04-25 Affected version: OpenClaw 2026.4.24 (cbcfdf6) Last known good version: 2026.4.14

Workaround applied on this machine (Watt)
A systemd drop-in disables the bonjour plugin so the gateway stays up:

~/.config/systemd/user/openclaw-gateway.service.d/disable-bonjour.conf
Contents:

[Service]
Environment=OPENCLAW_DISABLE_BONJOUR=1
Effect: startGatewayBonjourAdvertiser returns a no-op (see isDisabledByEnv() in dist/extensions/bonjour/index.js line ~94), so the watchdog never runs and the unhandled rejection never happens. Cost: LAN auto-discovery via openclaw.local mDNS is lost. Loopback (127.0.0.1:18789) and direct-IP access are unaffected — Telegram, hooks, agents, skills all work normally.

When to remove the drop-in
After upgrading to a version that fixes the bug (see "Suggested fix" below), it is safe to remove the override only if all of these are true:

The upstream changelog or commit history confirms the bonjour plugin now passes registerUnhandledRejectionHandler into deps at its advertise: callback in dist/extensions/bonjour/index.js.
After upgrading, with the drop-in temporarily kept in place, the gateway has been stable for at least 24h (systemctl --user show openclaw-gateway.service -p NRestarts --value stays at 0).
You actually want LAN auto-discovery back (phones / Macs finding openclaw.local on the LAN). If you do not use this, leaving the drop-in in place is harmless and removes a class of issues.
To remove:

rm ~/.config/systemd/user/openclaw-gateway.service.d/disable-bonjour.conf
rmdir ~/.config/systemd/user/openclaw-gateway.service.d/ 2>/dev/null
systemctl --user daemon-reload
systemctl --user restart openclaw-gateway.service
Then watch the logs for ~10 minutes:

journalctl --user -u openclaw-gateway.service -f
If you see bonjour: restarting advertiser followed by Unhandled promise rejection: CIAO ANNOUNCEMENT CANCELLED and the service exits, the bug is not actually fixed in that version — re-create the drop-in and revert.

Note on flaky mDNS underneath
Even after upstream patches the unhandled-rejection wiring, the underlying mDNS behavior on this LAN may still be flaky. Symptoms in the logs (with bonjour re-enabled) will look like:

[plugins] bonjour: watchdog detected non-announced service; attempting re-advertise (...)
[plugins] bonjour: advertised gateway ... state=announcing
[plugins] bonjour: restarting advertiser (service stuck in announcing for ~110000ms ...)
These are expected and benign post-fix — the watchdog is doing its job recycling a stuck advertiser, and the rejection is now caught by the plugin's classifier (CIAO_CANCELLATION_MESSAGE_RE). The gateway will not crash. It just means mDNS announcements aren't completing cleanly on this network — likely a router / multicast / IPv6-AAAA issue that bonjour can't work around.

If the chatter is annoying, leaving OPENCLAW_DISABLE_BONJOUR=1 in place is a reasonable permanent choice for this host since the gateway is reached by IP, not by openclaw.local.

Bug report (filed upstream)
Summary
On 2026.4.24, the gateway enters an unrecoverable crash loop on systems where the bonjour service stays in announcing state. The bonjour plugin defines a handler specifically to swallow ciao cancellation rejections, but the plugin entry point never passes the runtime's registerUnhandledRejectionHandler into deps, so the handler is never wired to the process. The watchdog's own recreateAdvertiser then triggers CIAO ANNOUNCEMENT CANCELLED, which goes unhandled and exits the process with status 1. systemd restarts and the cycle repeats forever.

Environment
OpenClaw 2026.4.24 (cbcfdf6)
Node 22.22.2, linux/arm64 (Arduino Uno Q, "watt-uno-q")
Was previously running 2026.4.14 with no issue
Symptoms
Every 3–4 minutes the gateway exits and is restarted by systemd:

[plugins] bonjour: advertised gateway ... state=announcing
[plugins] bonjour: restarting advertiser (service stuck in announcing for 112269ms ...)
[openclaw] Unhandled promise rejection: CIAO ANNOUNCEMENT CANCELLED
[openclaw] wrote stability bundle: .../openclaw-stability-...-unhandled_rejection.json
openclaw-gateway.service: Main process exited, code=exited, status=1/FAILURE
openclaw gateway status reports the service as running but the connectivity probe to ws://127.0.0.1:18789 returns ECONNREFUSED because the process is in the middle of restarting.

Root cause
In dist/extensions/bonjour/index.js:

The plugin defines handleCiaoUnhandledRejection (lines ~102–111) which is explicitly written to classify and ignore CIAO ANNOUNCEMENT|PROBING CANCELLED rejections via CIAO_CANCELLATION_MESSAGE_RE.

It only gets installed when the caller passes deps.registerUnhandledRejectionHandler:

// line ~153
cleanupUnhandledRejection: services.length > 0 && deps.registerUnhandledRejectionHandler
? deps.registerUnhandledRejectionHandler(handleCiaoUnhandledRejection)
: void 0
But the plugin entry point only passes logger:

// line ~287
advertise: async (ctx) => {
return { stop: (await startGatewayBonjourAdvertiser(
{ ...opts },
{ logger: api.logger } // <-- no handler dep passed
)).stop };
}
So the rejection handler is dead code. By contrast, extensions/telegram/probe-.js and extensions/whatsapp/monitor-.js both call registerUnhandledRejectionHandler correctly via the plugin SDK and don't crash on similar transient rejections.

How the crash gets triggered
svc.advertise() is called; service enters announcing state.
mDNS replies don't come back within the watchdog's stuck threshold (STUCK_ANNOUNCING_MS), so recreateAdvertiser(...) runs.
stopCycle(cycle) cancels the in-flight ciao announcement, which rejects with CIAO ANNOUNCEMENT CANCELLED.
No process-level handler is registered (see above) → unhandled rejection → process exits 1.
Suggested fix
Pass the handler registry from the plugin SDK into deps at the call site:

import { registerUnhandledRejectionHandler } from "openclaw/plugin-sdk/runtime-env";
...
advertise: async (ctx) => {
return { stop: (await startGatewayBonjourAdvertiser(
{ ...opts },
{ logger: api.logger, registerUnhandledRejectionHandler }
)).stop };
}
(Or expose it via api.* if that's the preferred plugin convention.)

After this fix the gateway should remain stable even when mDNS is flaky — the watchdog will silently restart the advertiser instead of crashing the gateway.

Stability bundle
~/.openclaw/logs/stability/openclaw-stability-*-unhandled_rejection.json (content is sparse — only metadata, no stack — likely because the stability recorder never captured events for the rejection itself.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdedupe:childDuplicate issue/PR child in dedupe clusterduplicateThis issue or pull request already existsregressionBehavior that previously worked and now fails

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions