Skip to content

session is getting logged out automatically and getting bad session error sometimes #1976

Description

@Pavan-Kumar-KN

Describe the bug
The WhatsApp server experiences continuous reconnection loops and connection state management issues when handling sessions, and session will logout automatically with showing reasons like "Bad Session" , and sometimes randomly the session will logout and getting this error (check below) the session socket is closing randomly without any reason

Failed to fetch groups for session: dev_device Error: Connection Closed
0|Whatsapp Server App  | 2025-10-27T09:33:07:     at sendRawMessage (file:///root/app/node_modules/baileys/lib/Socket/socket.js:50:19)
0|Whatsapp Server App  | 2025-10-27T09:33:07:     at sendNode (file:///root/app/node_modules/baileys/lib/Socket/socket.js:69:16)
0|Whatsapp Server App  | 2025-10-27T09:33:07:     at file:///root/app/node_modules/baileys/lib/Socket/socket.js:122:13
0|Whatsapp Server App  | 2025-10-27T09:33:07:     at new Promise (<anonymous>)
0|Whatsapp Server App  | 2025-10-27T09:33:07:     at promiseTimeout (file:///root/app/node_modules/baileys/lib/Utils/generics.js:111:16)
0|Whatsapp Server App  | 2025-10-27T09:33:07:     at query (file:///root/app/node_modules/baileys/lib/Socket/socket.js:120:30)
0|Whatsapp Server App  | 2025-10-27T09:33:07:     at Object.groupFetchAllParticipating (file:///root/app/node_modules/baileys/lib/Socket/groups.js:23:30)
0|Whatsapp Server App  | 2025-10-27T09:33:07:     at getAllGroups (file:///root/app/controllers/groupsController.js:20:38)
0|Whatsapp Server App  | 2025-10-27T09:33:07:     at process.processTicksAndRejections (node:internal/process/task_queues:105:5) {
0|Whatsapp Server App  | 2025-10-27T09:33:07:   data: null,
0|Whatsapp Server App  | 2025-10-27T09:33:07:   isBoom: true,
0|Whatsapp Server App  | 2025-10-27T09:33:07:   isServer: false,
0|Whatsapp Server App  | 2025-10-27T09:33:07:   output: {
0|Whatsapp Server App  | 2025-10-27T09:33:07:     statusCode: 428,
0|Whatsapp Server App  | 2025-10-27T09:33:07:     payload: {
0|Whatsapp Server App  | 2025-10-27T09:33:07:       statusCode: 428,
0|Whatsapp Server App  | 2025-10-27T09:33:07:       error: 'Precondition Required',
0|Whatsapp Server App  | 2025-10-27T09:33:07:       message: 'Connection Closed'
0|Whatsapp Server App  | 2025-10-27T09:33:07:     },
0|Whatsapp Server App  | 2025-10-27T09:33:07:     headers: {}
0|Whatsapp Server App  | 2025-10-27T09:33:07:   }
0|Whatsapp Server App  | 2025-10-27T09:33:07: }

To Reproduce
Steps to reproduce the behavior:

  1. Execute my above code and register one device and wait for an hour or try to fetch the group it will loop the
    connection or session socket will be closed multiple times
  2. Close the app & use saved credentials to log back in.

Expected behavior

  1. Session should not be closed automatically
  2. should not produce infinite loops

Environment (please complete the following information):

  • yes
  • [Please check the code below]
  • yes
  • no
import { rmSync, readdir } from 'fs'
import { join } from 'path'
import pino from 'pino'
import makeWASocket, {
    useMultiFileAuthState,
    Browsers,
    DisconnectReason,
    delay,
    makeCacheableSignalKeyStore,
    fetchLatestWaWebVersion
} from 'baileys';
import QrCode from 'qrcode'
import __dirname from './dirname.js'
import response from './response.js'
import axios from 'axios';


const sessions = new Map();
const sessionsStatus = new Map();
const retries = new Map();


const sessionsDir = (sessionId = '') => {
    return join(__dirname, 'sessions', sessionId ? sessionId : '')
}

const isSessionExists = (sessionId) => {
    const result = sessions.has(sessionId);
    return result;
}

const shouldReconnect = (sessionId) => {
    let maxRetries = parseInt(process.env.MAX_RETRIES ?? 0)
    let attempts = retries.get(sessionId) ?? 0

    maxRetries = maxRetries < 1 ? 1 : maxRetries

    if (attempts < maxRetries) {
        ++attempts

        console.log('Reconnecting...', { attempts, sessionId })
        retries.set(sessionId, attempts)

        return true
    }

    return false
}


async function createSession(sessionId, isLegacy = false, res) {

    // const groupCache = new NodeCache({ stdTTL: 5 * 60, useClones: false })
      const sessionFile = (isLegacy ? 'legacy_' : 'md_') + sessionId + (isLegacy ? '.json' : '')

    const { state, saveCreds } = await useMultiFileAuthState(sessionsDir(sessionFile));
    const { version } = await fetchLatestWaWebVersion();

    const sock = makeWASocket({
        version,
        auth: {
            creds: state.creds,
            keys: makeCacheableSignalKeyStore(state.keys)
        },
        logger: pino({ level: 'silent' }),
        keepAliveIntervalMs: 30_000,        // Reduced to 30s
        connectTimeoutMs: 60_000,           // Give more time to connect
        defaultQueryTimeoutMs: 60_000,
        browser: Browsers.windows('Whatsapp Server'),
        // cachedGroupMetadata: async (jid) => groupCache.get(jid),
        patchMessageBeforeSending: (message) => {
            const requiresPatch = !!(
                message.buttonsMessage ||
                message.listMessage
            );

            if (requiresPatch) {
                message = {
                    viewOnceMessage: {
                        message: {
                            messageContextInfo: {
                                deviceListMetadataVersion: 2,
                                deviceListMetadata: {},
                            },
                            ...message,
                        },
                    },
                };
            }

            return message;
        },
    });


    // store the credentials
    sock.ev.on('creds.update', saveCreds);

    sock.ev.on('chats.upsert', ({ chats }) => {
        // we have to handle this event when the chats is updated
    })

    // sock.ev.on('groups.update', async ([event]) => {
    //     const metadata = await sock.groupMetadata(event.id)
    //     groupCache.set(event.id, metadata)
    // })

    // sock.ev.on('group-participants.update', async (event) => {
    //     const metadata = await sock.groupMetadata(event.id)
    //     groupCache.set(event.id, metadata)
    // })

    sock.ev.on('connection.update', async (update) => {
        const { connection, lastDisconnect, qr } = update;
        const statusCode = lastDisconnect?.error?.output?.statusCode

        if (connection === 'open') {
            console.log(`[${sessionId}] Connected !`);
            retries.delete(sessionId); // RESET RETRIES
            sessionsStatus.set(sessionId, 'connected');
        }

        if (qr) {
            if (res && !res.headersSent) {
                try {
                    let qrImage = await QrCode.toDataURL(qr);

                    response(res, 200, true, 'QR Code received', { qr: qrImage });

                    // maintaining in memory active socket for this session
                    sessions.set(sessionId, sock);
                    return;
                } catch (error) {
                    if (!res.headersSent) {
                        sessionsStatus.set(sessionId, 'disconnected');
                        response(res, 500, false, 'Unable to create QR code.');
                    }
                }
            }
        }

        if (connection === 'close') {
            let reason = statusCode;

            // List of reconnectable reasons
            const reconnectable = [
                DisconnectReason.connectionClosed,
                DisconnectReason.connectionLost,
                DisconnectReason.timedOut,
                DisconnectReason.connectionReplaced,
                DisconnectReason.restartRequired,
                // DO NOT reconnect on loggedOut
            ];

            const shouldReconnectNow = reconnectable.includes(reason) && shouldReconnect(sessionId);

            if (shouldReconnectNow) {
                console.log(`[${sessionId}] Reconnecting... (Reason: ${DisconnectReason[reason] || reason}, Attempt: ${retries.get(sessionId)})`);
                sessionsStatus.set(sessionId, 'reconnecting');
                setTimeout(() => createSession(sessionId, isLegacy), 5000);
            } else {
                console.log(`[${sessionId}] Session terminated. Reason: ${DisconnectReason[reason] || reason}`);

                // Only logout if explicitly logged out
                if (reason === DisconnectReason.loggedOut) {
                     deleteSession(sessionId);
                    axios.post(process.env.DEVICE_UPDATE_WEBHOOK, {
                        "device_id": sessionId,
                        "status": "disconnected",
                        "reason": "logged out"
                    })
                        .then(response => console.log(response.status))
                        .catch(error => console.error(error.response.data));

                } else {
                    // Mark as disconnected but don't delete creds yet
                    sessionsStatus.set(sessionId, 'disconnected');
                }
            }
        }
    });

    // message listener
    sock.ev.on('messages.upsert', async (messages) => {
        try {
            const message = messages.messages[0];

            if (message.key.fromMe == false && messages.type == 'notify') {

                let parseId = message.key.remoteJid.split("@");
                let splitId = parseId[1] ?? null;

                let isGroup = splitId == 's.whatsapp.net' ? false : true;


                if (message != '' && isGroup == false) {
                    axios.post(process.env.WEBHOOK_URL, {
                        remote_id: message.key.remoteJid,
                        secret: process.env.WEBHOOK_SECRET,
                        from: message.key.remoteJid,
                        sessionId: sessionId,
                        message_id: message.key.id,
                        message: message.message,
                        timestamp: message.messageTimestamp,
                        type: message.message?.conversation ? 'text' : message.message?.extendedTextMessage?.contextInfo?.quotedMessage?.conversation ? 'quoted' : 'unknown',
                        quoted: message.message?.extendedTextMessage?.contextInfo?.quotedMessage?.conversation ? message.message?.extendedTextMessage?.contextInfo?.quotedMessage?.conversation : null,
                    })
                        .then(response => console.log(response.status))
                        .catch(error => console.error(error.response.data));
                }

            }
        }
        catch {

        }
    })

    return sock;
}


/**
 * @returns {(import('@adiwajshing/baileys').AnyWASocket|null)}
 */

const getSession = async (sessionId) => {
    if (!sessions.has(sessionId)) {
        console.log(`Rehydrating ${sessionId} from saved creds...`);

        // Create session
        const newSession = await createSession(sessionId);

        // Wait until connection is open
        await new Promise((resolve, reject) => {
            const onUpdate = (update) => {
                if (update.connection === 'open') {
                    newSession.ev.off('connection.update', onUpdate);
                    resolve(true);
                }
                if (update.connection === 'close') {
                    const code = update.lastDisconnect?.error?.output?.statusCode;
                    if (code && code !== DisconnectReason.loggedOut) {
                        // try reconnect logic if needed
                        return;
                    }
                    newSession.ev.off('connection.update', onUpdate);
                    reject(new Error('Connection closed before ready'));
                }
            };
            newSession.ev.on('connection.update', onUpdate);
        });

        sessions.set(sessionId, newSession);
    }

    return sessions.get(sessionId);
};

const getStatus = (sessionId) => {

    if (sessions.has(sessionId)) {
        return sessionsStatus.get(sessionId);
    }
    return null;
}

const deleteSession = (sessionId, isLegacy = false) => {
    const sessionFile = (isLegacy ? 'legacy_' : 'md_') + sessionId + (isLegacy ? '.json' : '')
    const storeFile = `${sessionId}_store.json`
    const rmOptions = { force: true, recursive: true }

    rmSync(sessionsDir(sessionFile), rmOptions)
    rmSync(sessionsDir(storeFile), rmOptions)

    sessions.delete(sessionId)
    retries.delete(sessionId)
    sessionsStatus.delete(sessionId);

    console.log(`${sessionId} connection is deleted`)
    // setDeviceStatus(sessionId, 0);
}

const getChatList = async (sessionId, isGroup = false) => {
    const filter = isGroup ? '@g.us' : '@s.whatsapp.net'

    const session = await getSession(sessionId);
    let data;

    if (isGroup) {
        data = Object.values(await session.groupFetchAllParticipating());
        return data;
    }


    data = Object.values(await session.fetchChats());

    return data;

    // * V!
    // return getSession(sessionId).store.chats.filter((chat) => {
    //     return chat.id.endsWith(filter)
    // })
}

/**
 * @param {import('@adiwajshing/baileys').AnyWASocket} session
 */
const isExists = async (session, jid, isGroup = false) => {
    try {
        let result

        if (isGroup) {
            result = await session.groupMetadata(jid)

            return Boolean(result.id)
        }

        if (session.isLegacy) {
            result = await session.onWhatsApp(jid)
        } else {
            ;[result] = await session.onWhatsApp(jid)
        }

        return result.exists
    } catch {
        return false
    }
}

/**
 * @param {import('@adiwajshing/baileys').AnyWASocket} session
 */
const sendMessage = async (session, receiver, message, delayMs = 1000) => {
    try {
        await delay(parseInt(delayMs))


        return session.sendMessage(receiver, message)
    } catch {
        return Promise.reject(null) // eslint-disable-line prefer-promise-reject-errors
    }
}

const formatPhone = (phone) => {
    phone = String(phone)
    if (phone.endsWith('@s.whatsapp.net')) {
        return phone
    }

    let formatted = phone.replace(/\D/g, '')

    return (formatted += '@s.whatsapp.net')
}

const formatGroup = (group) => {
    group = String(group)
    if (group.endsWith('@g.us')) {
        return group
    }

    let formatted = group.replace(/[^\d-]/g, '')

    return (formatted += '@g.us')
}

const cleanup = () => {
    console.log('Running cleanup before exit.')

    // sessions.forEach((session, sessionId) => {
    //     if (!session.isLegacy) {
    //         session.store.writeToFile(sessionsDir(`${sessionId}_store.json`))
    //     }
    // })
}

const init = () => {
    // readdir(sessionsDir(), (err, files) => {
    //     if (err) {
    //         throw err
    //     }

    //     for (const file of files) {
    //         if ((!file.startsWith('md_') && !file.startsWith('legacy_')) || file.endsWith('_store')) {
    //             continue
    //         }

    //         const filename = file.replace('.json', '')
    //         const isLegacy = filename.split('_', 1)[0] !== 'md'
    //         const sessionId = filename.substring(isLegacy ? 7 : 3)

    //         createSession(sessionId, isLegacy)
    //     }
    // })
}

export {
    isSessionExists,
    createSession,
    getSession,
    deleteSession,
    getChatList,
    isExists,
    sendMessage,
    formatPhone,
    formatGroup,
    cleanup,
    getStatus,
    init
}


Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    Status
    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions