fix(discord): classify current gateway fatal errors

This commit is contained in:
Peter Steinberger
2026-04-06 15:24:25 +01:00
parent f8a97881d1
commit 38543af3c4
3 changed files with 28 additions and 14 deletions

View File

@@ -6,21 +6,21 @@ import {
} from "./gateway-supervisor.js";
describe("classifyDiscordGatewayEvent", () => {
it("maps raw gateway errors onto domain events", () => {
it("maps current Carbon gateway errors onto domain events", () => {
const reconnectEvent = classifyDiscordGatewayEvent({
err: new Error("Max reconnect attempts (0) reached after code 1006"),
err: new Error("Max reconnect attempts (0) reached after close code 1006"),
isDisallowedIntentsError: () => false,
});
const fatalEvent = classifyDiscordGatewayEvent({
err: new Error("Fatal Gateway error: 4000"),
err: new Error("Fatal gateway close code: 4000"),
isDisallowedIntentsError: () => false,
});
const disallowedEvent = classifyDiscordGatewayEvent({
err: new Error("Fatal Gateway error: 4014"),
err: new Error("Fatal gateway close code: 4014"),
isDisallowedIntentsError: (err) => String(err).includes("4014"),
});
const transientEvent = classifyDiscordGatewayEvent({
err: new Error("transient"),
err: new TypeError(),
isDisallowedIntentsError: () => false,
});
@@ -28,8 +28,9 @@ describe("classifyDiscordGatewayEvent", () => {
expect(reconnectEvent.shouldStopLifecycle).toBe(true);
expect(fatalEvent.type).toBe("fatal");
expect(disallowedEvent.type).toBe("disallowed-intents");
expect(transientEvent.type).toBe("other");
expect(transientEvent.shouldStopLifecycle).toBe(false);
expect(transientEvent.type).toBe("fatal");
expect(transientEvent.message).toBe("TypeError");
expect(transientEvent.shouldStopLifecycle).toBe(true);
});
});
@@ -46,7 +47,7 @@ describe("createDiscordGatewaySupervisor", () => {
});
const seen: string[] = [];
emitter.emit("error", new Error("Fatal Gateway error: 4014"));
emitter.emit("error", new Error("Fatal gateway close code: 4014"));
expect(
supervisor.drainPending((event) => {
seen.push(event.type);
@@ -57,10 +58,10 @@ describe("createDiscordGatewaySupervisor", () => {
supervisor.attachLifecycle((event) => {
seen.push(event.type);
});
emitter.emit("error", new Error("Fatal Gateway error: 4000"));
emitter.emit("error", new Error("Fatal gateway close code: 4000"));
supervisor.detachLifecycle();
emitter.emit("error", new Error("Max reconnect attempts (0) reached after code 1006"));
emitter.emit("error", new Error("Max reconnect attempts (0) reached after close code 1006"));
expect(seen).toEqual(["disallowed-intents", "fatal"]);
expect(runtime.error).toHaveBeenCalledWith(
@@ -94,7 +95,7 @@ describe("createDiscordGatewaySupervisor", () => {
supervisor.dispose();
expect(() =>
emitter.emit("error", new Error("Max reconnect attempts (0) reached after code 1005")),
emitter.emit("error", new Error("Max reconnect attempts (0) reached after close code 1005")),
).not.toThrow();
expect(runtime.error).toHaveBeenCalledWith(
expect.stringContaining("suppressed late gateway reconnect-exhausted error after dispose"),

View File

@@ -1,6 +1,7 @@
import type { EventEmitter } from "node:events";
import { danger } from "openclaw/plugin-sdk/runtime-env";
import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
import { formatErrorMessage } from "openclaw/plugin-sdk/ssrf-runtime";
import { getDiscordGatewayEmitter } from "../monitor.gateway.js";
export type DiscordGatewayEventType =
@@ -32,7 +33,12 @@ export function classifyDiscordGatewayEvent(params: {
err: unknown;
isDisallowedIntentsError: (err: unknown) => boolean;
}): DiscordGatewayEvent {
const message = String(params.err);
const message =
params.err instanceof Error
? params.err.message
? `${params.err.name}: ${params.err.message}`
: params.err.name || "Error"
: formatErrorMessage(params.err);
if (params.isDisallowedIntentsError(params.err)) {
return {
type: "disallowed-intents",
@@ -49,7 +55,14 @@ export function classifyDiscordGatewayEvent(params: {
shouldStopLifecycle: true,
};
}
if (message.includes("Fatal Gateway error")) {
if (
params.err instanceof TypeError ||
message.includes("Fatal Gateway error") ||
message.includes("Fatal gateway close code") ||
message.includes("Gateway HELLO missing heartbeat") ||
message.includes("Invalid gateway payload") ||
message.includes("Gateway socket emitted an unknown error")
) {
return {
type: "fatal",
err: params.err,

View File

@@ -495,7 +495,7 @@ describe("monitorDiscordProvider", () => {
params.threadBindings.stop();
});
clientFetchUserMock.mockImplementationOnce(async () => {
emitter.emit("error", new Error("Fatal Gateway error: 4014"));
emitter.emit("error", new Error("Fatal gateway close code: 4014"));
return { id: "bot-1" };
});