diff --git a/CHANGELOG.md b/CHANGELOG.md index 2edf880b6c9..bba04f97956 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Plugins/Bonjour: stop the gateway from crash-looping on `CIAO PROBING CANCELLED` when the mDNS watchdog cancels a stuck probe. Restores the rejection-handler wiring dropped during the bonjour plugin migration and shares unhandled-rejection state across module instances so plugin-staged copies of `openclaw/plugin-sdk/runtime` register into the same handler set the host consults. Especially affects Docker on macOS, where mDNS probing reliably hits the watchdog. Thanks @troyhitch. - Plugins/startup: remove ownerless bundled runtime-dependency install locks after a short grace window and include lock owner details when startup times out waiting for a plugin runtime-deps lock. Thanks @steipete. diff --git a/extensions/bonjour/index.ts b/extensions/bonjour/index.ts index a02c7e2e612..ba39e0f874e 100644 --- a/extensions/bonjour/index.ts +++ b/extensions/bonjour/index.ts @@ -1,4 +1,5 @@ import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry"; +import { registerUnhandledRejectionHandler } from "openclaw/plugin-sdk/runtime"; import { startGatewayBonjourAdvertiser } from "./src/advertiser.js"; function formatBonjourInstanceName(displayName: string) { @@ -32,7 +33,7 @@ export default definePluginEntry({ cliPath: ctx.cliPath, minimal: ctx.minimal, }, - { logger: api.logger }, + { logger: api.logger, registerUnhandledRejectionHandler }, ); return { stop: advertiser.stop }; }, diff --git a/src/infra/unhandled-rejections.ts b/src/infra/unhandled-rejections.ts index 48f6f3d10de..ac26f490e7e 100644 --- a/src/infra/unhandled-rejections.ts +++ b/src/infra/unhandled-rejections.ts @@ -11,7 +11,20 @@ import { runFatalErrorHooks } from "./fatal-error-hooks.js"; type UnhandledRejectionHandler = (reason: unknown) => boolean; -const handlers = new Set(); +// Plugins resolve `openclaw/plugin-sdk/runtime` through their own staged +// `node_modules`, which loads a separate copy of this module. To keep registry +// state shared across instances, anchor the handlers Set on globalThis. +const HANDLERS_GLOBAL_KEY = Symbol.for("openclaw.unhandledRejection.handlers"); +const handlers: Set = (() => { + const g = globalThis as unknown as Record>; + const existing = g[HANDLERS_GLOBAL_KEY]; + if (existing instanceof Set) { + return existing; + } + const created = new Set(); + g[HANDLERS_GLOBAL_KEY] = created; + return created; +})(); const FATAL_ERROR_CODES = new Set([ "ERR_OUT_OF_MEMORY",