Submitted by: Ron at FlyTech
Summary
Baileys 7.0.0-rc.9 (bundled with OpenClaw) has three bugs in the authentication handshake that cause 100% connection failure. Every attempt fails with 401 Unauthorized - device_removed in a reconnect loop. This issue documents the root causes and provides the exact patches to fix them.
Environment
- OpenClaw with bundled Baileys
@whiskeysockets/baileys 7.0.0-rc.9
- Compared against known-working Baileys 6.17.16
Symptoms
[INFO] Connecting to WhatsApp...
[ERROR] Connection closed with reason: 401 Unauthorized - device_removed
[INFO] Reconnecting... (1/10)
[ERROR] Connection closed with reason: 401 Unauthorized - device_removed
Infinite reconnect loop. No pairing code is ever generated. Affects both QR and phone-number pairing flows.
Patch 1: Passive Flag — validate-connection.js line 53
File: lib/Utils/validate-connection.js
The generateLoginNode function sends passive: true in the client payload. WhatsApp's server interprets this as a passive listener and immediately terminates the connection with device_removed. It must be false for active device connections.
export const generateLoginNode = (userJid, config) => {
const { user, device } = jidDecode(userJid);
const payload = {
...getClientPayload(config),
- passive: true,
+ passive: false,
pull: true,
username: +user,
device: device,
Sed fix:
sudo sed -i 's/passive: true,/passive: false,/g' \
"$BAILEYS/Utils/validate-connection.js"
Patch 2: Remove Unknown lidDbMigrated Field — validate-connection.js line 58
File: lib/Utils/validate-connection.js
RC9 added lidDbMigrated: false to the login payload. This field doesn't exist in WhatsApp's protocol spec — the server rejects payloads containing it. The source even has a // TODO: investigate comment, suggesting it was experimental.
username: +user,
device: device,
// TODO: investigate (hard set as false atm)
- lidDbMigrated: false
};
return proto.ClientPayload.fromObject(payload);
};
Sed fix:
sudo sed -i '/lidDbMigrated: false/d' \
"$BAILEYS/Utils/validate-connection.js"
Patch 3: Noise Protocol Timing — socket.js line 324
File: lib/Socket/socket.js
RC9 incorrectly awaits noise.finishInit() after sending the client finish frame. This creates a race condition: the keep-alive request fires before the noise handshake state is fully committed. Removing the await matches the working 6.17.16 behavior.
}
}).finish());
- await noise.finishInit();
+ noise.finishInit();
startKeepAliveRequest();
};
Sed fix:
sudo sed -i 's/await noise\.finishInit();/noise.finishInit();/g' \
"$BAILEYS/Socket/socket.js"
Complete Fix Script
#!/bin/bash
# baileys-rc9-fix.sh — Fixes auth in Baileys 7.0.0-rc.9 bundled with OpenClaw
set -e
BAILEYS="/usr/lib/node_modules/openclaw/node_modules/@whiskeysockets/baileys/lib"
if [ ! -d "$BAILEYS" ]; then
echo "❌ Baileys not found at $BAILEYS — update the path for your installation"
exit 1
fi
echo "📦 Creating backups..."
sudo cp "$BAILEYS/Utils/validate-connection.js" "$BAILEYS/Utils/validate-connection.js.orig"
sudo cp "$BAILEYS/Socket/socket.js" "$BAILEYS/Socket/socket.js.orig"
echo "🔨 Patch 1: passive: true → false"
sudo sed -i 's/passive: true,/passive: false,/g' "$BAILEYS/Utils/validate-connection.js"
echo "🔨 Patch 2: removing lidDbMigrated field"
sudo sed -i '/lidDbMigrated: false/d' "$BAILEYS/Utils/validate-connection.js"
echo "🔨 Patch 3: noise.finishInit() timing"
sudo sed -i 's/await noise\.finishInit();/noise.finishInit();/g' "$BAILEYS/Socket/socket.js"
echo "✅ All patches applied. Restart your OpenClaw gateway to take effect."
echo "💾 Originals saved with .orig extension"
Verification
After patching, confirm with:
# Patch 1
grep 'passive:' "$BAILEYS/Utils/validate-connection.js"
# Should show: passive: false,
# Patch 2
grep 'lidDbMigrated' "$BAILEYS/Utils/validate-connection.js"
# Should return nothing
# Patch 3
grep 'noise.finishInit' "$BAILEYS/Socket/socket.js"
# Should show: noise.finishInit(); (no await)
Phone Number Pairing
With patches applied, phone-number pairing works. Key requirements:
- Browser string must be exactly
['Mac OS', 'Chrome', '14.4.1']
defaultQueryTimeoutMs: undefined during pairing to prevent premature timeout
useMultiFileAuthState() for session persistence across restarts
- On
badSession disconnect, clear auth directory and retry
const sock = makeWASocket({
auth: state,
browser: ['Mac OS', 'Chrome', '14.4.1'],
printQRInTerminal: false,
defaultQueryTimeoutMs: undefined,
});
// Request pairing code when QR is available
sock.ev.on('connection.update', async ({ qr, connection }) => {
if (qr) {
const code = await sock.requestPairingCode(phoneNumber);
console.log(`Enter this code in WhatsApp > Linked Devices: ${code}`);
}
if (connection === 'open') {
console.log('✅ Connected!');
}
});
sock.ev.on('creds.update', saveCreds);
Suggested Action
Consider either:
- Pinning OpenClaw's bundled Baileys to a known-working version (e.g., 6.17.16)
- Applying these three patches to the RC9 bundle in the OpenClaw distribution
These are surgical fixes — they only correct the three specific auth regressions without side effects.
Submitted by: Ron at FlyTech
Summary
Baileys 7.0.0-rc.9 (bundled with OpenClaw) has three bugs in the authentication handshake that cause 100% connection failure. Every attempt fails with
401 Unauthorized - device_removedin a reconnect loop. This issue documents the root causes and provides the exact patches to fix them.Environment
@whiskeysockets/baileys7.0.0-rc.9Symptoms
Infinite reconnect loop. No pairing code is ever generated. Affects both QR and phone-number pairing flows.
Patch 1: Passive Flag —
validate-connection.jsline 53File:
lib/Utils/validate-connection.jsThe
generateLoginNodefunction sendspassive: truein the client payload. WhatsApp's server interprets this as a passive listener and immediately terminates the connection withdevice_removed. It must befalsefor active device connections.export const generateLoginNode = (userJid, config) => { const { user, device } = jidDecode(userJid); const payload = { ...getClientPayload(config), - passive: true, + passive: false, pull: true, username: +user, device: device,Sed fix:
Patch 2: Remove Unknown
lidDbMigratedField —validate-connection.jsline 58File:
lib/Utils/validate-connection.jsRC9 added
lidDbMigrated: falseto the login payload. This field doesn't exist in WhatsApp's protocol spec — the server rejects payloads containing it. The source even has a// TODO: investigatecomment, suggesting it was experimental.username: +user, device: device, // TODO: investigate (hard set as false atm) - lidDbMigrated: false }; return proto.ClientPayload.fromObject(payload); };Sed fix:
Patch 3: Noise Protocol Timing —
socket.jsline 324File:
lib/Socket/socket.jsRC9 incorrectly
awaitsnoise.finishInit()after sending the client finish frame. This creates a race condition: the keep-alive request fires before the noise handshake state is fully committed. Removing theawaitmatches the working 6.17.16 behavior.} }).finish()); - await noise.finishInit(); + noise.finishInit(); startKeepAliveRequest(); };Sed fix:
Complete Fix Script
Verification
After patching, confirm with:
Phone Number Pairing
With patches applied, phone-number pairing works. Key requirements:
['Mac OS', 'Chrome', '14.4.1']defaultQueryTimeoutMs: undefinedduring pairing to prevent premature timeoutuseMultiFileAuthState()for session persistence across restartsbadSessiondisconnect, clear auth directory and retrySuggested Action
Consider either:
These are surgical fixes — they only correct the three specific auth regressions without side effects.