Baileys uses the pino logger for debug output. This page covers how to configure logging levels, troubleshoot common issues, and debug your integration.
Logger Configuration
Baileys uses Pino as its logging library. You can configure the logger when creating a socket connection.
Log Levels
Pino supports these log levels (from least to most verbose):
silent - No output
fatal - Fatal errors only
error - Errors only
warn - Warnings and errors
info - Informational messages (default)
debug - Debug information
trace - Extremely detailed trace information
Basic Logger Setup
import makeWASocket from '@whiskeysockets/baileys'
import P from 'pino'
const sock = makeWASocket({
logger: P({ level: 'debug' })
})
Silent Mode (Testing)
For unit tests or when you want no output:
const sock = makeWASocket({
logger: P({ level: 'silent' })
})
Pretty-Printed Logs
Use pino-pretty for human-readable console output:
import P from 'pino'
const logger = P({
level: 'trace',
transport: {
target: 'pino-pretty',
options: { colorize: true }
}
})
const sock = makeWASocket({ logger })
You need to install pino-pretty separately: npm install pino-pretty
Debug Level Output
Enabling Debug Logs
Debug level shows unhandled messages from WhatsApp:
const sock = makeWASocket({
logger: P({ level: 'debug' })
})
With debug enabled, you’ll see output like:
{
"unhandled": true,
"msgId": "1234.5678-9",
"fromMe": false,
"frame": {
"tag": "ib",
"attrs": { "from": "@s.whatsapp.net" },
"content": [...]
},
"msg": "communication recv"
}
This helps you identify messages that Baileys isn’t handling, which you can then process with custom handlers.
Trace Level Output
Enabling Trace Logs
Trace level shows the XML representation of ALL binary nodes sent and received:
const sock = makeWASocket({
logger: P({ level: 'trace' })
})
What Trace Shows
You’ll see two types of trace messages:
Received messages:
{xml: '<iq id="123" type="result">...</iq>', msg: 'recv xml'}
Sent messages:
{xml: '<iq id="456" type="get">...</iq>', msg: 'xml send'}
Trace level produces MASSIVE amounts of output. Use it only for debugging specific protocol issues.
Trace in Source Code
From src/Socket/socket.ts:
// When sending a node
const sendNode = (frame: BinaryNode) => {
if (logger.level === 'trace') {
logger.trace({ xml: binaryNodeToString(frame), msg: 'xml send' })
}
// ...
}
// When receiving a frame
if (logger.level === 'trace') {
logger.trace({ xml: binaryNodeToString(frame), msg: 'recv xml' })
}
Multi-Transport Logging
Log to both console and file:
import P from 'pino'
const logger = P({
level: 'trace',
transport: {
targets: [
{
target: 'pino-pretty',
options: { colorize: true },
level: 'trace'
},
{
target: 'pino/file',
options: { destination: './wa-logs.txt' },
level: 'trace'
}
]
}
})
const sock = makeWASocket({ logger })
This logs pretty output to console and raw JSON to wa-logs.txt.
Common Debugging Scenarios
Debug Connection Issues
import P from 'pino'
const logger = P({ level: 'debug' })
const sock = makeWASocket({ logger })
sock.ev.on('connection.update', (update) => {
console.log('Connection update:', update)
if (update.connection === 'close') {
const { error } = update.lastDisconnect || {}
console.error('Connection closed:', error)
}
})
Debug Message Sending
Enable trace to see the exact XML being sent:
const sock = makeWASocket({
logger: P({ level: 'trace' })
})
await sock.sendMessage(jid, { text: 'Hello' })
// You'll see the XML representation of the message in logs
Debug Authentication
Log credential updates:
sock.ev.on('creds.update', (update) => {
console.log('Credentials updated:', Object.keys(update))
})
Debug Message Reception
sock.ev.on('messages.upsert', ({ messages, type }) => {
console.log('Messages received:', type, messages.length)
for (const msg of messages) {
console.log('Message:', JSON.stringify(msg, null, 2))
}
})
Troubleshooting Common Issues
Issue: No Messages Received
Check:
- Connection status is
'open'
- You’re listening to the correct event:
messages.upsert
- Offline messages are processed:
receivedPendingNotifications: true
sock.ev.on('connection.update', ({ connection, receivedPendingNotifications }) => {
console.log('Connection:', connection)
console.log('Offline msgs processed:', receivedPendingNotifications)
})
Issue: Cannot Send Messages
Enable debug logs:
const sock = makeWASocket({
logger: P({ level: 'debug' })
})
try {
await sock.sendMessage(jid, { text: 'test' })
} catch (error) {
console.error('Send failed:', error)
}
Common causes:
- Socket not open yet
- Invalid JID format
- Pre-keys not uploaded
- Session expired
Issue: Pre-Key Upload Errors
// Check pre-key count on server
const sock = makeWASocket({
logger: P({ level: 'debug' })
})
// Look for logs like:
// "uploading pre-keys" - Pre-keys being uploaded
// "uploaded pre-keys successfully" - Success
// "Failed to upload pre-keys to server" - Error
Issue: Connection Keeps Closing
Check the disconnect reason:
import { DisconnectReason } from '@whiskeysockets/baileys'
import { Boom } from '@hapi/boom'
sock.ev.on('connection.update', ({ connection, lastDisconnect }) => {
if (connection === 'close') {
const statusCode = (lastDisconnect?.error as Boom)?.output?.statusCode
switch(statusCode) {
case DisconnectReason.badSession:
console.log('Bad Session File, Delete and Scan Again')
break
case DisconnectReason.connectionClosed:
console.log('Connection closed, reconnecting...')
break
case DisconnectReason.connectionLost:
console.log('Connection Lost from Server, reconnecting...')
break
case DisconnectReason.connectionReplaced:
console.log('Connection Replaced, Another New Session Opened')
break
case DisconnectReason.loggedOut:
console.log('Device Logged Out, Delete and Scan Again')
break
case DisconnectReason.restartRequired:
console.log('Restart Required, Restarting...')
break
case DisconnectReason.timedOut:
console.log('Connection TimedOut, Reconnecting...')
break
default:
console.log('Unknown disconnect reason:', statusCode)
}
}
})
Debugging Custom Functionality
When writing custom handlers, log extensively:
import { BinaryNode } from '@whiskeysockets/baileys'
sock.ws.on('CB:ib,,custom-event', (node: BinaryNode) => {
console.log('Custom event received')
console.log('Tag:', node.tag)
console.log('Attrs:', node.attrs)
console.log('Content:', node.content)
try {
// Your custom logic
} catch (error) {
console.error('Error in custom handler:', error)
}
})
Monitor Event Processing
sock.ev.on('messages.upsert', async ({ messages }) => {
const start = Date.now()
for (const msg of messages) {
await processMessage(msg)
}
console.log(`Processed ${messages.length} messages in ${Date.now() - start}ms`)
})
Monitor Memory Usage
setInterval(() => {
const used = process.memoryUsage()
console.log('Memory usage:', {
rss: `${Math.round(used.rss / 1024 / 1024)}MB`,
heapUsed: `${Math.round(used.heapUsed / 1024 / 1024)}MB`
})
}, 60000) // Every minute
Best Practices
Development: Use level: 'debug' to see all unhandled messagesProduction: Use level: 'warn' or level: 'error' to reduce noiseDebugging specific issues: Use level: 'trace' temporarily
Structured Logging
const logger = P({ level: 'debug' })
// Good: Structured data
logger.info({ user: jid, msgId }, 'message sent')
// Avoid: String concatenation
logger.info('message sent to ' + jid + ' with id ' + msgId)
Error Context
try {
await sock.sendMessage(jid, { text: 'hello' })
} catch (error) {
logger.error({ error, jid }, 'failed to send message')
}
Next Steps