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: }
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
}
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
To Reproduce
Steps to reproduce the behavior:
connection or session socket will be closed multiple times
Expected behavior
Environment (please complete the following information):