From 88012c69d1b7f8e121d672fbaa1044d2f24a6a45 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 9 Feb 2026 16:33:54 +0000 Subject: [PATCH 1/2] improve: clarify app state sync log when decryption key is unavailable Changes the log message when an app-state-sync key is not found (404) to clearly indicate this is expected behavior for new sessions, not an error. Old encryption keys from previous sessions are not shared by the WhatsApp server to newly paired devices. Before: "failed to sync state from version" (looks like an error) After: "app state sync: decryption key not available for X -- expected for new sessions where old keys are not shared by the server" https://claude.ai/code/session_01XaA7GwNaB6azTHFYQ8WEpB --- src/Socket/chats.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/Socket/chats.ts b/src/Socket/chats.ts index 759227b1..c1118e27 100644 --- a/src/Socket/chats.ts +++ b/src/Socket/chats.ts @@ -638,14 +638,23 @@ export const makeChatsSocket = (config: SocketConfig) => { } catch (error: any) { // if retry attempts overshoot // or key not found + const isKeyNotFound = error.output?.statusCode === 404 const isIrrecoverableError = (attemptsMap[name] || 0) >= MAX_SYNC_ATTEMPTS || - error.output?.statusCode === 404 || + isKeyNotFound || error.name === 'TypeError' - logger.info( - { name, error: error.stack }, - `failed to sync state from version${isIrrecoverableError ? '' : ', removing and trying from scratch'}` - ) + if (isKeyNotFound) { + const currentVersion = states[name]?.version ?? 0 + logger.info( + { name }, + `app state sync: decryption key not available for "${name}" (syncing from v${currentVersion}) -- expected for new sessions where old keys are not shared by the server` + ) + } else { + logger.info( + { name, error: error.stack }, + `failed to sync state from version${isIrrecoverableError ? '' : ', removing and trying from scratch'}` + ) + } await authState.keys.set({ 'app-state-sync-version': { [name]: null } }) // increment number of retries attemptsMap[name] = (attemptsMap[name] || 0) + 1 From 0cac0a18594b6a832f3cbf8a8b6a22d11e34b919 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 9 Feb 2026 17:53:09 +0000 Subject: [PATCH 2/2] improve: add warning logs to null guards and fix transferDevice error handling - Add warning/debug logs to all null guard patterns (if (!x) return/continue) so that when these guards fire, the reason is visible in logs instead of being silently swallowed - Fix jid-utils.ts transferDevice: throw Error instead of returning empty string '' which could propagate as an invalid JID - process-message.ts: warn on creds.me missing, reactionKey missing, creationMsgKey missing, eventCreatorPn missing - chats.ts: warn on sendPresenceUpdate/handlePresenceUpdate missing values - event-buffer.ts: debug on chat/contact/group update missing id - socket.ts: debug on sessionStartTime not set - communities.ts: debug on dirty node not found - libsignal.ts: warn on bulk migration jidDecode failure https://claude.ai/code/session_01XaA7GwNaB6azTHFYQ8WEpB --- src/Signal/libsignal.ts | 1 + src/Socket/chats.ts | 27 ++++++++++++++++++++++----- src/Socket/communities.ts | 1 + src/Socket/socket.ts | 1 + src/Utils/event-buffer.ts | 22 ++++++++++++++++++---- src/Utils/process-message.ts | 4 ++++ src/WABinary/jid-utils.ts | 2 +- 7 files changed, 48 insertions(+), 10 deletions(-) diff --git a/src/Signal/libsignal.ts b/src/Signal/libsignal.ts index 93fadafc..43c03cbf 100644 --- a/src/Signal/libsignal.ts +++ b/src/Signal/libsignal.ts @@ -495,6 +495,7 @@ export function makeLibSignalRepository( const decoded1 = jidDecode(fromJid) if (!decoded1) { + logger.warn({ fromJid }, 'bulkDeviceMigration: failed to decode fromJid, aborting migration') return { migrated: 0, skipped: 0, total: 0 } } diff --git a/src/Socket/chats.ts b/src/Socket/chats.ts index c1118e27..88bc853d 100644 --- a/src/Socket/chats.ts +++ b/src/Socket/chats.ts @@ -754,9 +754,16 @@ export const makeChatsSocket = (config: SocketConfig) => { }) } } else { - if (!toJid) return + if (!toJid) { + logger.warn('sendPresenceUpdate: toJid is missing, skipping') + return + } + const decoded = jidDecode(toJid) - if (!decoded) return + if (!decoded) { + logger.warn({ toJid }, 'sendPresenceUpdate: failed to decode toJid, skipping') + return + } const { server } = decoded const isLid = server === 'lid' @@ -798,7 +805,10 @@ export const makeChatsSocket = (config: SocketConfig) => { let presence: PresenceData | undefined const jid = attrs.from const participant = attrs.participant || attrs.from - if (!jid) return + if (!jid) { + logger.warn({ attrs }, 'handlePresenceUpdate: jid (attrs.from) is missing, skipping') + return + } if (shouldIgnoreJid(jid) && jid !== S_WHATSAPP_NET) { return @@ -811,7 +821,10 @@ export const makeChatsSocket = (config: SocketConfig) => { } } else if (Array.isArray(content)) { const [firstChild] = content - if (!firstChild) return + if (!firstChild) { + logger.warn({ jid }, 'handlePresenceUpdate: firstChild content is empty, skipping') + return + } let type = firstChild.tag as WAPresence if (type === 'paused') { type = 'available' @@ -827,7 +840,11 @@ export const makeChatsSocket = (config: SocketConfig) => { } if (presence) { - if (!participant) return + if (!participant) { + logger.warn({ jid }, 'handlePresenceUpdate: participant is missing, skipping') + return + } + ev.emit('presence.update', { id: jid, presences: { [participant]: presence } }) } } diff --git a/src/Socket/communities.ts b/src/Socket/communities.ts index 0c78397b..7cb02d07 100644 --- a/src/Socket/communities.ts +++ b/src/Socket/communities.ts @@ -102,6 +102,7 @@ export const makeCommunitiesSocket = (config: SocketConfig) => { sock.ws.on('CB:ib,,dirty', async (node: BinaryNode) => { const dirtyNode = getBinaryNodeChild(node, 'dirty') if (!dirtyNode) { + logger.debug({ node: node.tag }, 'community dirty handler: no dirty node found, skipping') return } diff --git a/src/Socket/socket.ts b/src/Socket/socket.ts index 0b1a9563..70861049 100644 --- a/src/Socket/socket.ts +++ b/src/Socket/socket.ts @@ -886,6 +886,7 @@ export const makeSocket = (config: SocketConfig) => { } if (!sessionStartTime) { + logger.debug('TTL timer: sessionStartTime not set, skipping') return } diff --git a/src/Utils/event-buffer.ts b/src/Utils/event-buffer.ts index 208153f6..698c7d43 100644 --- a/src/Utils/event-buffer.ts +++ b/src/Utils/event-buffer.ts @@ -639,6 +639,8 @@ export const makeEventBuffer = ( if (updateId) { newData.chatUpdates[updateId] = update delete data.chatUpdates[updateId] + } else { + logger.debug({ update }, 'conditional chat update missing id, not carrying forward') } } } @@ -979,7 +981,10 @@ function append( case 'chats.update': for (const update of eventData as ChatUpdate[]) { const chatId = update.id - if (!chatId) continue + if (!chatId) { + logger.debug({ update }, 'chats.update: update missing id, skipping') + continue + } const conditionMatches = update.conditional ? update.conditional(data) : true if (conditionMatches) { delete update.conditional @@ -1057,7 +1062,10 @@ function append( const contactUpdates = eventData as BaileysEventMap['contacts.update'] for (const update of contactUpdates) { const id = update.id - if (!id) continue + if (!id) { + logger.debug({ update }, 'contacts.update: update missing id, skipping') + continue + } // merge into prior upsert const upsert = data.historySets.contacts[id] || data.contactUpserts[id] if (upsert) { @@ -1179,7 +1187,10 @@ function append( const groupUpdates = eventData as BaileysEventMap['groups.update'] for (const update of groupUpdates) { const id = update.id - if (!id) continue + if (!id) { + logger.debug({ update }, 'groups.update: update missing id, skipping') + continue + } const groupUpdate = data.groupUpdates[id] || {} if (!data.groupUpdates[id]) { data.groupUpdates[id] = Object.assign(groupUpdate, update) @@ -1212,7 +1223,10 @@ function append( // decrement chat unread counter // if the message has already been marked read by us const chatId = message.key.remoteJid - if (!chatId) return + if (!chatId) { + logger.debug({ messageKey: message.key }, 'decrementChatReadCounter: remoteJid missing, skipping') + return + } const chat = data.chatUpdates[chatId] || data.chatUpserts[chatId] if ( isRealMessage(message) && diff --git a/src/Utils/process-message.ts b/src/Utils/process-message.ts index a3604860..78a27a7b 100644 --- a/src/Utils/process-message.ts +++ b/src/Utils/process-message.ts @@ -288,6 +288,7 @@ const processMessage = async ( ) => { const meUser = creds.me if (!meUser) { + logger?.warn({ messageKey: message.key }, 'processMessage: creds.me not set, skipping message') return } @@ -548,6 +549,7 @@ const processMessage = async ( } else if (content?.reactionMessage) { const reactionKey = content.reactionMessage.key if (!reactionKey) { + logger?.warn({ messageKey: message.key }, 'processMessage: reactionMessage.key missing, skipping') return } @@ -565,6 +567,7 @@ const processMessage = async ( const encEventResponse = content.encEventResponseMessage const creationMsgKey = encEventResponse.eventCreationMessageKey if (!creationMsgKey) { + logger?.warn({ messageKey: message.key }, 'processMessage: eventCreationMessageKey missing, skipping') return } @@ -580,6 +583,7 @@ const processMessage = async ( ? await signalRepository.lidMapping.getPNForLID(eventCreatorKey) : eventCreatorKey if (!eventCreatorPn) { + logger?.warn({ messageKey: message.key, eventCreatorKey }, 'processMessage: eventCreatorPn missing, skipping') return } diff --git a/src/WABinary/jid-utils.ts b/src/WABinary/jid-utils.ts index 0c42456f..8cdf4379 100644 --- a/src/WABinary/jid-utils.ts +++ b/src/WABinary/jid-utils.ts @@ -131,7 +131,7 @@ export const transferDevice = (fromJid: string, toJid: string) => { const deviceId = fromDecoded?.device || 0 const toDecoded = jidDecode(toJid) if (!toDecoded) { - return '' + throw new Error(`transferDevice: failed to decode toJid "${toJid}"`) } const { server, user } = toDecoded