* fix: break infinite Bad MAC + session recreation loop
Remove aggressive session cleanup from the decryption hot path that caused
cascading failures when multiple messages from the same contact arrived
simultaneously. The Signal Protocol naturally recovers via retry+pkmsg flow.
Changes:
- Remove cleanupCorruptedSession() from per-message error handler (hot path)
- Move session cleanup to retry-exhausted handler as safety net
- Add per-JID retry request deduplication (5s window)
- Add 10s cooldown on MAC error session recreation
- Export cleanupCorruptedSession/getDecryptionJid for use in messages-recv
Tested: corrupted session with fake keys → Bad MAC → retry receipt →
pkmsg recovery → all 7 messages delivered, zero infinite loop.
* fix: address PR review — lint, dedup scope, and cleanup targeting
- Remove unused `autoCleanCorrupted` param from decryptMessageNode (lint fix)
- Scope retry dedup by participant JID in group chats (not group JID)
- Move dedup registration after early-return checks to avoid blocking
subsequent messages when max retries reached
- Use participant JID for session cleanup in groups (Signal sessions
are per-participant, not per-group)
* feat: centralized LID→PN normalization for all emitted events
WhatsApp's server increasingly uses LID (Linked ID) as the primary
addressing format. Consumers expect phone numbers (PN),
not opaque LID identifiers. This adds comprehensive LID→PN resolution
across all ev.emit() paths so downstream consumers always receive PN.
Key changes:
- Add resolveLidToPn() and normalizeKeyLidToPn() centralized helpers
in process-message.ts for consistent LID→PN resolution
- normalizeMessageJids() fast path: use alt JID directly from stanza
attributes (zero I/O, eliminates race condition with LIDMappingStore)
- Await storeLIDPNMappings() before normalization in message receipt
flow (was fire-and-forget, could race with subsequent getPNForLID)
- Normalize LID→PN in all event emission points:
* messages.upsert (keys, nested reaction/poll keys, participantAlt)
* presence.update (jid + participant)
* message-receipt.update (key + userJid)
* contacts.update (picture notifications)
* blocklist.update (blocklist JIDs)
* call events (chatId, from)
* group metadata (participants, owner, subjectOwner)
* group notifications (acting participant, add/remove/promote/demote)
* newsletter notifications (author, user JIDs)
* sync actions (mutation index normalization)
- Make handlePresenceUpdate and handleGroupNotification async to
support await on LID resolution
- Add normalizeGroupMetadata() helper in groups.ts for all
extractGroupMetadata call sites
Performance: ~0.01ms per message (LRU cache hit). No impact on
message sending. First-contact resolution ~2-5ms (one-time per contact).
* fix: normalize LID→PN in handleBadAck, media retry, and PDO recovery
Additional leak points found during final audit:
- handleBadAck: key.remoteJid from ack stanza could be LID
- messages.media-update: media retry key JIDs not normalized
- CTWA PDO recovery: webMessageInfo.key from phone response not normalized
* fix: address PR review — parallelize LID resolution, use helper consistently
- normalizeGroupMetadata: resolve participant LIDs with Promise.all
instead of sequential loop (perf on large groups)
- handleCall: resolve participant JIDs with Promise.all
- handleBadAck: use normalizeKeyLidToPn() helper instead of manual
resolveLidToPn + assignment (consistency with other code paths)
- CB:relay: resolve callCreator LID→PN before emitting
* fix: add timeout to fetchLatestBaileysVersion to prevent hanging connections
AbortController with 5s default timeout prevents indefinite blocking
when GitHub is unreachable (DNS failure, network issues). Falls back
to bundled version via existing catch block. Ref: Baileys#2385.
* fix: move clearTimeout to finally block in fetchLatestBaileysVersion
Ensures timer cleanup on both success and fetch rejection paths,
preventing dangling timers when DNS/network fails before abort fires.
In DMs, when the connected user sends a reaction, the inner key's
remoteJid could remain unnormalized (e.g. LID vs PN), preventing
correct matching with the original message. Also normalises participant
in groups for own messages — an improvement over upstream Baileys#2386.
* feat: apply PR #2339 missing commits (ced3305 + fe5a37c)
Apply two Feb-19 commits from WhiskeySockets/Baileys PR #2339 that
were missing from the initial integration:
ced3305 — Prevent tctoken attachment to peer (AppStateSync) messages
- Add isPeerMessage guard: additionalAttributes?.['category'] === 'peer'
- Exclude peer messages from is1on1Send to fix error 479 (SmaxInvalid)
which was causing hundreds of rejections on multi-device sync JIDs
fe5a37c — Error 463 retry logic + blocking→non-blocking refactor
- Replace blocking await getPrivacyTokens() with fire-and-forget .then/.catch
so the send path is never delayed by a token fetch
- Add tcTokenRetriedMsgIds Set in messages-recv.ts to track retried msgIds
- On error 463 (MissingTcToken): wait 1.5s then relay the message once,
allowing the server's tctoken notification to arrive before resend
- Add retry_463_ok log event to baileys-logger for observability