mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-29 02:41:07 +00:00
discord: fix stale-socket reconnect crash from uncaught reconnect-exhausted error
This commit is contained in:
committed by
Peter Steinberger
parent
9f8c4efa9b
commit
5b85d0efa4
@@ -62,6 +62,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Agents/failover: classify Codex accountId token extraction failures as auth errors so model fallback continues to the next configured candidate. (#55206) Thanks @cosmicnet.
|
||||
- Talk/macOS: stop direct system-voice failures from replaying system speech, use app-locale fallback for shared watchdog timing, and add regression coverage for the macOS fallback route and language-aware timeout policy. (#53511) thanks @hongsw.
|
||||
- Discord/gateway cleanup: keep late Carbon reconnect-exhausted errors suppressed through startup/dispose cleanup so Discord monitor shutdown no longer crashes on late gateway close events. (#55373) Thanks @Takhoffman.
|
||||
- Discord/gateway shutdown: treat expected reconnect-exhausted events during intentional lifecycle stop as clean shutdowns so startup-abort cleanup no longer surfaces false gateway failures. (#55324) Thanks @joelnishanth.
|
||||
|
||||
## 2026.3.24
|
||||
|
||||
|
||||
@@ -806,6 +806,51 @@ describe("runDiscordGatewayLifecycle", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("suppresses reconnect-exhausted as expected during intentional shutdown", async () => {
|
||||
const { runDiscordGatewayLifecycle } = await import("./provider.lifecycle.js");
|
||||
const pendingGatewayEvents: DiscordGatewayEvent[] = [];
|
||||
const abortController = new AbortController();
|
||||
|
||||
const emitter = new EventEmitter();
|
||||
const gateway = {
|
||||
isConnected: true,
|
||||
options: { reconnect: { maxAttempts: 50 } },
|
||||
disconnect: vi.fn(),
|
||||
connect: vi.fn(),
|
||||
emitter,
|
||||
};
|
||||
getDiscordGatewayEmitterMock.mockReturnValueOnce(emitter);
|
||||
|
||||
const { lifecycleParams, runtimeLog, runtimeError } = createLifecycleHarness({
|
||||
gateway,
|
||||
pendingGatewayEvents,
|
||||
});
|
||||
lifecycleParams.abortSignal = abortController.signal;
|
||||
|
||||
// Start lifecycle; it yields at execApprovalsHandler.start(). We then
|
||||
// queue a reconnect-exhausted event and abort. The lifecycle resumes,
|
||||
// drains the event (with lifecycleStopping=true), and exits cleanly
|
||||
// without reaching waitForDiscordGatewayStop.
|
||||
const lifecyclePromise = runDiscordGatewayLifecycle(lifecycleParams);
|
||||
|
||||
pendingGatewayEvents.push(
|
||||
createGatewayEvent(
|
||||
"reconnect-exhausted",
|
||||
"Max reconnect attempts (0) reached after code 1005",
|
||||
),
|
||||
);
|
||||
abortController.abort();
|
||||
|
||||
await expect(lifecyclePromise).resolves.toBeUndefined();
|
||||
|
||||
expect(runtimeLog).toHaveBeenCalledWith(
|
||||
expect.stringContaining("ignoring expected reconnect-exhausted during shutdown"),
|
||||
);
|
||||
expect(runtimeError).not.toHaveBeenCalledWith(
|
||||
expect.stringContaining("Max reconnect attempts"),
|
||||
);
|
||||
});
|
||||
|
||||
it("does not push connected: true when abortSignal is already aborted", async () => {
|
||||
const { runDiscordGatewayLifecycle } = await import("./provider.lifecycle.js");
|
||||
const emitter = new EventEmitter();
|
||||
|
||||
@@ -485,6 +485,15 @@ export async function runDiscordGatewayLifecycle(params: {
|
||||
);
|
||||
return "stop";
|
||||
}
|
||||
// When we deliberately set maxAttempts=0 and disconnected (health-monitor
|
||||
// stale-socket restart), Carbon fires "Max reconnect attempts (0)". This
|
||||
// is expected — log at info instead of error to avoid false alarms.
|
||||
if (lifecycleStopping && event.type === "reconnect-exhausted") {
|
||||
params.runtime.log?.(
|
||||
`discord: ignoring expected reconnect-exhausted during shutdown: ${event.message}`,
|
||||
);
|
||||
return "stop";
|
||||
}
|
||||
params.runtime.error?.(danger(`discord gateway error: ${event.message}`));
|
||||
return event.shouldStopLifecycle ? "stop" : "continue";
|
||||
};
|
||||
@@ -494,7 +503,13 @@ export async function runDiscordGatewayLifecycle(params: {
|
||||
if (decision !== "stop") {
|
||||
return "continue";
|
||||
}
|
||||
if (event.type === "disallowed-intents") {
|
||||
// Don't throw for expected shutdown events — intentional disconnect
|
||||
// (reconnect-exhausted with maxAttempts=0) and disallowed-intents are
|
||||
// both handled without crashing the provider.
|
||||
if (
|
||||
event.type === "disallowed-intents" ||
|
||||
(lifecycleStopping && event.type === "reconnect-exhausted")
|
||||
) {
|
||||
return "stop";
|
||||
}
|
||||
throw event.err;
|
||||
|
||||
Reference in New Issue
Block a user