fix(bonjour): suppress ciao cancellation across plugin runtime copies

Fix the bundled Bonjour gateway discovery crash-loop caused by ciao probe cancellation rejections after the Bonjour plugin migration.

The plugin entry now wires the existing rejection handler into the advertiser, and the unhandled-rejection handler registry is anchored on globalThis so staged plugin SDK module copies register into the same process-level handler set used by the host.

Verification:
- pnpm test:serial extensions/bonjour/src/advertiser.test.ts src/infra/unhandled-rejections.fatal-detection.test.ts
- OPENCLAW_LOCAL_CHECK_MODE=throttled pnpm check:changed partially completed: conflict markers plus core/core-test/extensions/extension-test typecheck passed; local lint lane hit a self-lock and was stopped.
This commit is contained in:
Troy Hitch
2026-04-25 14:38:30 -04:00
committed by GitHub
parent 888448facc
commit 63241bf1e0
3 changed files with 17 additions and 2 deletions

View File

@@ -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.

View File

@@ -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 };
},

View File

@@ -11,7 +11,20 @@ import { runFatalErrorHooks } from "./fatal-error-hooks.js";
type UnhandledRejectionHandler = (reason: unknown) => boolean;
const handlers = new Set<UnhandledRejectionHandler>();
// 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<UnhandledRejectionHandler> = (() => {
const g = globalThis as unknown as Record<symbol, Set<UnhandledRejectionHandler>>;
const existing = g[HANDLERS_GLOBAL_KEY];
if (existing instanceof Set) {
return existing;
}
const created = new Set<UnhandledRejectionHandler>();
g[HANDLERS_GLOBAL_KEY] = created;
return created;
})();
const FATAL_ERROR_CODES = new Set([
"ERR_OUT_OF_MEMORY",