From 93e2d90af174cc600f0e48818cc321647775dc8d Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 2 May 2026 08:24:04 +0100 Subject: [PATCH] fix(discord): reconnect after missed identify --- CHANGELOG.md | 1 + .../discord/src/internal/gateway.test.ts | 36 +++++++++++++++++++ extensions/discord/src/internal/gateway.ts | 15 ++++++-- 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd0baf7c05a..18341be637b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Discord/gateway: reconnect when the gateway socket closes while waiting for the shared IDENTIFY concurrency window, instead of silently skipping IDENTIFY and leaving the bot online but unresponsive. Fixes #74617. Thanks @zeeskdr-ai. - Telegram/startup: use the existing `getMe` request guard for the gateway bot probe instead of a fixed 2.5-second budget, and honor higher `timeoutSeconds` configs for slow Telegram API paths. Fixes #75783. Thanks @tankotan. - Infer/media: report missing image-understanding and audio-transcription provider configuration for `image describe`, `image describe-many`, and `audio transcribe` instead of blaming the input path when no provider is available. Fixes #73569 and supersedes #73593, #74288, and #74495. Thanks @bittoby, @tmimmanuel, @Linux2010, and @vyctorbrzezowski. - Docs/health: clarify that session listing surfaces stored conversation rows rather than Discord/channel socket liveness, and point connectivity checks at channel status and health probes. Fixes #70420. Thanks @ashersoutherncities-art and @martingarramon. diff --git a/extensions/discord/src/internal/gateway.test.ts b/extensions/discord/src/internal/gateway.test.ts index a7ebf3aedcd..8001a8c34a9 100644 --- a/extensions/discord/src/internal/gateway.test.ts +++ b/extensions/discord/src/internal/gateway.test.ts @@ -127,6 +127,42 @@ describe("GatewayPlugin", () => { await vi.waitFor(() => expect(errorSpy).toHaveBeenCalledWith(error)); }); + it("reconnects when the socket closes while waiting for identify concurrency", async () => { + vi.useFakeTimers(); + vi.setSystemTime(0); + await sharedGatewayIdentifyLimiter.wait({ shardId: 0, maxConcurrency: 1 }); + const gateway = new TestGatewayPlugin({ + autoInteractions: false, + url: "wss://gateway.example.test", + }); + const errorSpy = vi.fn(); + gateway.emitter.on("error", errorSpy); + + gateway.connect(false); + const socket = gateway.sockets[0]; + socket?.emit("open"); + socket?.emit( + "message", + JSON.stringify({ + op: GatewayOpcodes.Hello, + d: { heartbeat_interval: 45_000 }, + s: null, + }), + ); + if (socket) { + socket.readyState = 3; + } + + await vi.advanceTimersByTimeAsync(5_000); + expect(errorSpy).toHaveBeenCalledWith( + new Error("Discord gateway socket closed before IDENTIFY could be sent"), + ); + await vi.advanceTimersByTimeAsync(2_000); + + expect(gateway.connectCalls).toEqual([false, false]); + expect(gateway.sockets).toHaveLength(2); + }); + it("preserves MESSAGE_CREATE author payloads for inbound dispatch", async () => { const gateway = new GatewayPlugin({ autoInteractions: false }); const dispatchGatewayEvent = vi.fn(async (_event: string, _data: unknown) => {}); diff --git a/extensions/discord/src/internal/gateway.ts b/extensions/discord/src/internal/gateway.ts index 5308e169a38..8c6c5cf616c 100644 --- a/extensions/discord/src/internal/gateway.ts +++ b/extensions/discord/src/internal/gateway.ts @@ -248,7 +248,12 @@ export class GatewayPlugin extends Plugin { true, ); } else { - void this.identifyWithConcurrency(); + void this.identifyWithConcurrency().catch((error: unknown) => { + this.emitter.emit( + "error", + error instanceof Error ? error : new Error(String(error), { cause: error }), + ); + }); } break; case GatewayOpcodes.HeartbeatAck: @@ -325,7 +330,13 @@ export class GatewayPlugin extends Plugin { shardId: this.shardId, maxConcurrency: this.gatewayInfo?.session_start_limit.max_concurrency, }); - if (!this.ws || this.ws.readyState !== READY_STATE_OPEN) { + const socket = this.ws; + if (!socket || socket.readyState !== READY_STATE_OPEN) { + const error = new Error("Discord gateway socket closed before IDENTIFY could be sent"); + this.emitter.emit("error", error); + if (socket) { + this.scheduleReconnect(false); + } return; } this.identify();