From 72ca397cc85ec6a191248b21b13ce2025a902bb7 Mon Sep 17 00:00:00 2001 From: Danilo Leone Date: Wed, 8 Apr 2026 19:24:43 -0300 Subject: [PATCH] fix: logout instance --- .../whatsapp/whatsapp.baileys.service.ts | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 8dfb94433..c49591491 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -284,7 +284,7 @@ export class BaileysStartupService extends ChannelStartupService { } public async logoutInstance() { - // Mark instance as deleting to prevent reconnection attempts + // Mark instance as deleting to prevent reconnection attempts. this.isDeleting = true; this.endSession = true; @@ -294,18 +294,27 @@ export class BaileysStartupService extends ChannelStartupService { try { await this.client.logout('Log out instance: ' + this.instanceName); } catch (error) { - this.logger.error({ message: 'Error during logout', error }); + // Downgraded to warn: logout failures here are recoverable — the + // credential cleanup below still runs and the DB row is forced to 'close'. + this.logger.warn( + `logoutInstance: client.logout() failed (${(error as Error)?.message}), proceeding with credential cleanup`, + ); } - // Improved socket cleanup + // Improved socket cleanup. try { this.client.ws?.close(); this.client.end(new Error('Instance logout')); - } catch (error) { - this.logger.error({ message: 'Error during socket cleanup', error }); + } catch { + // ignore — ws may already be closed } } + // Force the in-memory connection state to 'close' so any concurrent reader + // observes the post-logout state immediately, even if the DB update below + // is delayed. + this.stateConnection = { state: 'close', statusReason: 401 }; + const db = this.configService.get('DATABASE'); const cache = this.configService.get('CACHE'); const provider = this.configService.get('PROVIDER'); @@ -332,6 +341,11 @@ export class BaileysStartupService extends ChannelStartupService { if (sessionExists) { await this.prismaRepository.session.delete({ where: { sessionId: this.instanceId } }); } + + await this.prismaRepository.instance.update({ + where: { id: this.instanceId }, + data: { connectionStatus: 'close' }, + }); } public async getProfileName() { @@ -479,7 +493,9 @@ export class BaileysStartupService extends ChannelStartupService { } const statusCode = (lastDisconnect?.error as Boom)?.output?.statusCode; - const codesToNotReconnect = [DisconnectReason.loggedOut, DisconnectReason.forbidden, 402, 406]; + // 408 = request timeout — added per #2501 to avoid reconnect loops on + // transient network drops where the server returned a 408 in the close. + const codesToNotReconnect = [DisconnectReason.loggedOut, DisconnectReason.forbidden, 402, 406, 408]; // FIX: Do not reconnect if it's the initial connection (waiting for QR code) // This prevents infinite loop that blocks QR code generation @@ -542,6 +558,10 @@ export class BaileysStartupService extends ChannelStartupService { } if (connection === 'open') { + if (!this.client?.user?.id) { + this.logger.warn('connectionUpdate: connection open but client.user is undefined, skipping'); + return; + } this.instance.wuid = this.client.user.id.replace(/:\d+/, ''); try { const profilePic = await this.profilePicture(this.instance.wuid);