mirror of https://github.com/openclaw/openclaw.git
fix(gateway): suppress ciao interface assertions
Closes #38628. Refs #47159, #52431. Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
parent
3faaf8984f
commit
c0d4abc59e
|
|
@ -257,6 +257,7 @@ Docs: https://docs.openclaw.ai
|
|||
<<<<<<< HEAD
|
||||
- WhatsApp/reconnect: preserve the last inbound timestamp across reconnect attempts so the watchdog can still recycle linked-but-dead listeners after a restart instead of leaving them stuck connected forever.
|
||||
- Gateway/network discovery: guard LAN, tailnet, and pairing interface enumeration so WSL2 and restricted hosts degrade to missing-address fallbacks instead of crashing on `uv_interface_addresses` errors. (#44180, #47590)
|
||||
- Gateway/bonjour: suppress the non-fatal `@homebridge/ciao` IPv4-loss assertion during interface churn so WiFi/VPN/sleep-wake changes no longer take down the gateway. (#38628, #47159, #52431)
|
||||
|
||||
### Breaking
|
||||
|
||||
|
|
|
|||
|
|
@ -1,47 +1,70 @@
|
|||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
const logDebugMock = vi.hoisted(() => vi.fn());
|
||||
const logWarnMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("../logger.js", () => ({
|
||||
logDebug: (...args: unknown[]) => logDebugMock(...args),
|
||||
logWarn: (...args: unknown[]) => logWarnMock(...args),
|
||||
}));
|
||||
|
||||
const { ignoreCiaoCancellationRejection } = await import("./bonjour-ciao.js");
|
||||
const { ignoreCiaoUnhandledRejection } = await import("./bonjour-ciao.js");
|
||||
|
||||
describe("bonjour-ciao", () => {
|
||||
it("ignores and logs ciao announcement cancellation rejections", () => {
|
||||
expect(
|
||||
ignoreCiaoCancellationRejection(new Error("Ciao announcement cancelled by shutdown")),
|
||||
).toBe(true);
|
||||
expect(logDebugMock).toHaveBeenCalledWith(
|
||||
expect.stringContaining("ignoring unhandled ciao rejection"),
|
||||
);
|
||||
});
|
||||
|
||||
it("ignores and logs ciao probing cancellation rejections", () => {
|
||||
logDebugMock.mockReset();
|
||||
|
||||
expect(ignoreCiaoCancellationRejection(new Error("CIAO PROBING CANCELLED"))).toBe(true);
|
||||
expect(logDebugMock).toHaveBeenCalledWith(
|
||||
expect.stringContaining("ignoring unhandled ciao rejection"),
|
||||
);
|
||||
});
|
||||
|
||||
it("ignores lower-case string cancellation reasons too", () => {
|
||||
logDebugMock.mockReset();
|
||||
|
||||
expect(ignoreCiaoCancellationRejection("ciao announcement cancelled during cleanup")).toBe(
|
||||
expect(ignoreCiaoUnhandledRejection(new Error("Ciao announcement cancelled by shutdown"))).toBe(
|
||||
true,
|
||||
);
|
||||
expect(logDebugMock).toHaveBeenCalledWith(
|
||||
expect.stringContaining("ignoring unhandled ciao rejection"),
|
||||
);
|
||||
expect(logWarnMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("ignores and logs ciao probing cancellation rejections", () => {
|
||||
logDebugMock.mockReset();
|
||||
logWarnMock.mockReset();
|
||||
|
||||
expect(ignoreCiaoUnhandledRejection(new Error("CIAO PROBING CANCELLED"))).toBe(true);
|
||||
expect(logDebugMock).toHaveBeenCalledWith(
|
||||
expect.stringContaining("ignoring unhandled ciao rejection"),
|
||||
);
|
||||
expect(logWarnMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("ignores lower-case string cancellation reasons too", () => {
|
||||
logDebugMock.mockReset();
|
||||
logWarnMock.mockReset();
|
||||
|
||||
expect(ignoreCiaoUnhandledRejection("ciao announcement cancelled during cleanup")).toBe(true);
|
||||
expect(logDebugMock).toHaveBeenCalledWith(
|
||||
expect.stringContaining("ignoring unhandled ciao rejection"),
|
||||
);
|
||||
expect(logWarnMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("suppresses ciao interface assertion rejections as non-fatal", () => {
|
||||
logDebugMock.mockReset();
|
||||
logWarnMock.mockReset();
|
||||
|
||||
const error = Object.assign(
|
||||
new Error("Reached illegal state! IPV4 address change from defined to undefined!"),
|
||||
{ name: "AssertionError" },
|
||||
);
|
||||
|
||||
expect(ignoreCiaoUnhandledRejection(error)).toBe(true);
|
||||
expect(logWarnMock).toHaveBeenCalledWith(
|
||||
expect.stringContaining("suppressing ciao interface assertion"),
|
||||
);
|
||||
expect(logDebugMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("keeps unrelated rejections visible", () => {
|
||||
logDebugMock.mockReset();
|
||||
logWarnMock.mockReset();
|
||||
|
||||
expect(ignoreCiaoCancellationRejection(new Error("boom"))).toBe(false);
|
||||
expect(ignoreCiaoUnhandledRejection(new Error("boom"))).toBe(false);
|
||||
expect(logDebugMock).not.toHaveBeenCalled();
|
||||
expect(logWarnMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,13 +1,21 @@
|
|||
import { logDebug } from "../logger.js";
|
||||
import { logDebug, logWarn } from "../logger.js";
|
||||
import { formatBonjourError } from "./bonjour-errors.js";
|
||||
|
||||
const CIAO_CANCELLATION_MESSAGE_RE = /^CIAO (?:ANNOUNCEMENT|PROBING) CANCELLED\b/u;
|
||||
const CIAO_INTERFACE_ASSERTION_MESSAGE_RE =
|
||||
/REACHED ILLEGAL STATE!?\s+IPV4 ADDRESS CHANGE FROM DEFINED TO UNDEFINED!?/u;
|
||||
|
||||
export function ignoreCiaoCancellationRejection(reason: unknown): boolean {
|
||||
const message = formatBonjourError(reason).toUpperCase();
|
||||
export function ignoreCiaoUnhandledRejection(reason: unknown): boolean {
|
||||
const formatted = formatBonjourError(reason);
|
||||
const message = formatted.toUpperCase();
|
||||
if (!CIAO_CANCELLATION_MESSAGE_RE.test(message)) {
|
||||
return false;
|
||||
if (!CIAO_INTERFACE_ASSERTION_MESSAGE_RE.test(message)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
logWarn(`bonjour: suppressing ciao interface assertion: ${formatted}`);
|
||||
return true;
|
||||
}
|
||||
logDebug(`bonjour: ignoring unhandled ciao rejection: ${formatBonjourError(reason)}`);
|
||||
logDebug(`bonjour: ignoring unhandled ciao rejection: ${formatted}`);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { logDebug, logWarn } from "../logger.js";
|
||||
import { getLogger } from "../logging.js";
|
||||
import { ignoreCiaoCancellationRejection } from "./bonjour-ciao.js";
|
||||
import { ignoreCiaoUnhandledRejection } from "./bonjour-ciao.js";
|
||||
import { formatBonjourError } from "./bonjour-errors.js";
|
||||
import { isTruthyEnvValue } from "./env.js";
|
||||
import { registerUnhandledRejectionHandler } from "./unhandled-rejections.js";
|
||||
|
|
@ -175,7 +175,7 @@ export async function startGatewayBonjourAdvertiser(
|
|||
|
||||
const cleanupUnhandledRejection =
|
||||
services.length > 0
|
||||
? registerUnhandledRejectionHandler(ignoreCiaoCancellationRejection)
|
||||
? registerUnhandledRejectionHandler(ignoreCiaoUnhandledRejection)
|
||||
: undefined;
|
||||
|
||||
return { responder, services, cleanupUnhandledRejection };
|
||||
|
|
|
|||
Loading…
Reference in New Issue