fix(gateway): improve crash resilience for mDNS and network errors
This commit addresses several gateway crash issues:
1. Expand Bonjour error handling (bonjour-ciao.ts):
- Add patterns for transient mDNS errors:
- 'REACHED ILLEGAL STATE' (IPv4 address changes)
- 'IPV4/IPV6 ADDRESS CHANGED'
- 'NETWORK INTERFACE' changes
- Handle AssertionError from MDNServer during network churn
- Fixes #3821 - mDNS crash loops on sleep/wake
2. Improve network error detection (unhandled-rejections.ts):
- Add ERR_ASSERTION to transient network codes
- Detect AssertionError + 'IPV4 ADDRESS CHANGED' pattern
- Better handling of @homebridge/ciao errors
- Fixes #3815 - unhandled fetch failures crashing gateway
3. Add --no-bonjour CLI flag (gateway-cli/run.ts):
- New option to disable mDNS/Bonjour advertising
- Sets OPENCLAW_DISABLE_BONJOUR=1 env var
- Provides workaround for #3821 while root cause is fixed
Closes #3821, #3815
This commit is contained in:
parent
6af205a13a
commit
6454842bd3
@ -48,6 +48,7 @@ type GatewayRunOpts = {
|
||||
rawStreamPath?: unknown;
|
||||
dev?: boolean;
|
||||
reset?: boolean;
|
||||
bonjour?: boolean;
|
||||
};
|
||||
|
||||
const gatewayLog = createSubsystemLogger("gateway");
|
||||
@ -89,6 +90,11 @@ async function runGatewayCommand(opts: GatewayRunOpts) {
|
||||
process.env.OPENCLAW_RAW_STREAM_PATH = rawStreamPath;
|
||||
}
|
||||
|
||||
// Handle --no-bonjour flag to disable mDNS/Bonjour advertising
|
||||
if (opts.bonjour === false) {
|
||||
process.env.OPENCLAW_DISABLE_BONJOUR = "1";
|
||||
}
|
||||
|
||||
if (devMode) {
|
||||
await ensureDevGatewayConfig({ reset: Boolean(opts.reset) });
|
||||
}
|
||||
@ -350,6 +356,7 @@ export function addGatewayRunCommand(cmd: Command): Command {
|
||||
.option("--compact", 'Alias for "--ws-log compact"', false)
|
||||
.option("--raw-stream", "Log raw model stream events to jsonl", false)
|
||||
.option("--raw-stream-path <path>", "Raw stream jsonl path")
|
||||
.option("--no-bonjour", "Disable mDNS/Bonjour service advertising", false)
|
||||
.action(async (opts) => {
|
||||
await runGatewayCommand(opts);
|
||||
});
|
||||
|
||||
@ -2,11 +2,31 @@ import { logDebug } from "../logger.js";
|
||||
|
||||
import { formatBonjourError } from "./bonjour-errors.js";
|
||||
|
||||
// Error patterns from @homebridge/ciao that should not crash the gateway
|
||||
// These are typically transient network/mDNS issues that resolve on their own
|
||||
const BONJOUR_TRANSIENT_ERRORS = [
|
||||
"CIAO ANNOUNCEMENT CANCELLED",
|
||||
"REACHED ILLEGAL STATE", // IPv4 address changes during network interface churn
|
||||
"IPV4 ADDRESS CHANGED",
|
||||
"IPV6 ADDRESS CHANGED",
|
||||
"MDNSSERVER",
|
||||
"NETWORK INTERFACE", // Network interface changes (sleep/wake, WiFi reconnect)
|
||||
];
|
||||
|
||||
export function ignoreCiaoCancellationRejection(reason: unknown): boolean {
|
||||
const message = formatBonjourError(reason).toUpperCase();
|
||||
if (!message.includes("CIAO ANNOUNCEMENT CANCELLED")) {
|
||||
const errorName = reason instanceof Error ? reason.name?.toUpperCase() : "";
|
||||
|
||||
// Check for transient mDNS/Bonjour error patterns
|
||||
const isTransientError = BONJOUR_TRANSIENT_ERRORS.some((pattern) => message.includes(pattern));
|
||||
|
||||
// Also catch AssertionError from MDNServer (common during network changes)
|
||||
const isAssertionError = errorName === "ASSERTIONERROR" && message.includes("MDNS");
|
||||
|
||||
if (!isTransientError && !isAssertionError) {
|
||||
return false;
|
||||
}
|
||||
|
||||
logDebug(`bonjour: ignoring unhandled ciao rejection: ${formatBonjourError(reason)}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -34,6 +34,8 @@ const TRANSIENT_NETWORK_CODES = new Set([
|
||||
"UND_ERR_SOCKET",
|
||||
"UND_ERR_HEADERS_TIMEOUT",
|
||||
"UND_ERR_BODY_TIMEOUT",
|
||||
// mDNS/Bonjour errors from @homebridge/ciao
|
||||
"ERR_ASSERTION", // IPv4 address changes during network interface churn
|
||||
]);
|
||||
|
||||
function getErrorCause(err: unknown): unknown {
|
||||
@ -99,6 +101,23 @@ export function isTransientNetworkError(err: unknown): boolean {
|
||||
return err.errors.some((e) => isTransientNetworkError(e));
|
||||
}
|
||||
|
||||
// Handle mDNS/Bonjour errors from @homebridge/ciao
|
||||
// These are typically triggered by network interface changes (sleep/wake, WiFi reconnect)
|
||||
if (err instanceof Error) {
|
||||
const errorName = err.name?.toUpperCase() || "";
|
||||
const message = err.message?.toUpperCase() || "";
|
||||
|
||||
// AssertionError from MDNServer during network interface changes
|
||||
if (errorName === "ASSERTIONERROR" && message.includes("IPV4 ADDRESS CHANGED")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Other mDNS-related transient errors
|
||||
if (message.includes("MDNSSERVER") && message.includes("ILLEGAL STATE")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user