mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:50:43 +00:00
fix: keep bonjour failures non-fatal
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user