fix: keep bonjour failures non-fatal

This commit is contained in:
Peter Steinberger
2026-04-26 23:27:02 +01:00
parent c6cf37068c
commit 8a60e57846
4 changed files with 71 additions and 11 deletions

View File

@@ -24,7 +24,6 @@ AGENT_TURN_TIMEOUT_SECONDS="${OPENCLAW_INSTALL_E2E_AGENT_TURN_TIMEOUT_SECONDS:-6
export NPM_CONFIG_PREFIX="${NPM_CONFIG_PREFIX:-$HOME/.npm-global}"
mkdir -p "$NPM_CONFIG_PREFIX"
export PATH="$NPM_CONFIG_PREFIX/bin:$PATH"
export OPENCLAW_DISABLE_BONJOUR="${OPENCLAW_DISABLE_BONJOUR:-1}"
if [[ "$MODELS_MODE" != "both" && "$MODELS_MODE" != "openai" && "$MODELS_MODE" != "anthropic" ]]; then
echo "ERROR: OPENCLAW_E2E_MODELS must be one of: both|openai|anthropic" >&2

View File

@@ -196,6 +196,32 @@ describe("installUnhandledRejectionHandler - fatal detection", () => {
);
});
it("does not exit on known Bonjour dependency failures", () => {
const bonjourCases: unknown[] = [
new Error("CIAO ANNOUNCEMENT CANCELLED"),
new Error("CIAO PROBING CANCELLED"),
Object.assign(
new Error("Reached illegal state! IPV4 address change from defined to undefined!"),
{ name: "AssertionError" },
),
Object.assign(
new Error(
"IP address version must match. Netmask cannot have a version different from the address!",
),
{ name: "AssertionError" },
),
];
for (const bonjourErr of bonjourCases) {
expectExitCodeFromUnhandled(bonjourErr, []);
}
expect(consoleWarnSpy).toHaveBeenCalledWith(
"[openclaw] Non-fatal unhandled rejection (continuing):",
expect.stringContaining("CIAO ANNOUNCEMENT CANCELLED"),
);
});
it("exits on generic errors without code", () => {
const genericErr = new Error("Something went wrong");

View File

@@ -116,6 +116,12 @@ const TRANSIENT_SQLITE_MESSAGE_SNIPPETS = [
"disk i/o error",
];
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|UNDEFINED TO DEFINED)!?/u;
const CIAO_NETMASK_ASSERTION_MESSAGE_RE =
/IP ADDRESS VERSION MUST MATCH\.\s+NETMASK CANNOT HAVE A VERSION DIFFERENT FROM THE ADDRESS!?/u;
function hasSqliteSignal(err: unknown): boolean {
if (!err || typeof err !== "object") {
return false;
@@ -335,8 +341,46 @@ export function isTransientSqliteError(err: unknown): boolean {
return false;
}
export function isKnownBonjourDependencyError(err: unknown): boolean {
if (!err) {
return false;
}
for (const candidate of collectNestedUnhandledErrorCandidates(err)) {
const rawMessage =
candidate && typeof candidate === "object"
? (candidate as { message?: unknown }).message
: undefined;
const message =
typeof candidate === "string"
? candidate
: candidate && typeof candidate === "object"
? typeof rawMessage === "string"
? rawMessage
: ""
: "";
const normalized = message.trim().toUpperCase();
if (!normalized) {
continue;
}
if (
CIAO_CANCELLATION_MESSAGE_RE.test(normalized) ||
CIAO_INTERFACE_ASSERTION_MESSAGE_RE.test(normalized) ||
CIAO_NETMASK_ASSERTION_MESSAGE_RE.test(normalized)
) {
return true;
}
}
return false;
}
export function isTransientUnhandledRejectionError(err: unknown): boolean {
return isTransientNetworkError(err) || isTransientSqliteError(err);
return (
isTransientNetworkError(err) ||
isTransientSqliteError(err) ||
isKnownBonjourDependencyError(err)
);
}
export function registerUnhandledRejectionHandler(handler: UnhandledRejectionHandler): () => void {

View File

@@ -3,7 +3,6 @@ import { describe, expect, it } from "vitest";
const SCRIPT_PATH = "scripts/test-install-sh-docker.sh";
const SMOKE_RUNNER_PATH = "scripts/docker/install-sh-smoke/run.sh";
const E2E_RUNNER_PATH = "scripts/docker/install-sh-e2e/run.sh";
const BUN_GLOBAL_SMOKE_PATH = "scripts/e2e/bun-global-install-smoke.sh";
const INSTALL_SMOKE_WORKFLOW_PATH = ".github/workflows/install-smoke.yml";
const RELEASE_CHECKS_WORKFLOW_PATH = ".github/workflows/openclaw-release-checks.yml";
@@ -129,14 +128,6 @@ describe("install-sh smoke runner", () => {
});
});
describe("install-sh e2e runner", () => {
it("disables Bonjour for Docker loopback gateway checks", () => {
const script = readFileSync(E2E_RUNNER_PATH, "utf8");
expect(script).toContain('export OPENCLAW_DISABLE_BONJOUR="${OPENCLAW_DISABLE_BONJOUR:-1}"');
});
});
describe("bun global install smoke", () => {
it("packs the current tree and verifies image-provider discovery through Bun", () => {
const script = readFileSync(BUN_GLOBAL_SMOKE_PATH, "utf8");