From c46889db435cb69ead2c778c371dd65fc0c1b637 Mon Sep 17 00:00:00 2001 From: Renato Alcara Date: Sat, 25 Apr 2026 13:08:54 -0300 Subject: [PATCH] fix(messages-recv): plain ack and error handling on early-ignore path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses Codex P1 + Copilot + CodeRabbit review on PR #383: 1. Use plain sendMessageAck(node) for ignored messages — InfiniteAPI's prior in-handler check did NOT use NACK_REASONS.UnhandledError. The error code reports a processing failure and can trigger server-side retries; ignored stanzas are an intentional drop, not a failure. Restoring the original semantics. 2. Wrap the early-ignore block in try/catch routing to onUnexpectedError. shouldIgnoreJid is a user-provided callback and sendMessageAck can throw (e.g. websocket closed). Without this guard, the error becomes an unhandled promise rejection because the early path runs outside processNodeWithBuffer's catch wrapper. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/Socket/messages-recv.ts | 38 ++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/src/Socket/messages-recv.ts b/src/Socket/messages-recv.ts index bbaf0e54..77aa0118 100644 --- a/src/Socket/messages-recv.ts +++ b/src/Socket/messages-recv.ts @@ -3091,21 +3091,33 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { // Fast path: ack and drop ignored JIDs before entering the buffer/queue. // Skips type='call' so call events are never silently dropped via // shouldIgnoreJid (preserves InfiniteAPI's pre-existing behavior). + // Wrapped in try/catch so a throw from shouldIgnoreJid (user callback) + // or sendMessageAck (e.g. websocket closed) is routed through + // onUnexpectedError instead of becoming an unhandled rejection — + // matches the protection processNodeWithBuffer provides. if (type !== 'call') { - const from = node.attrs.from - let ignoreJid = from - if (type === 'receipt' && from) { - const attrs = node.attrs - const isLid = attrs.from!.includes('lid') - const isNodeFromMe = areJidsSameUser( - attrs.participant || attrs.from, - isLid ? authState.creds.me?.lid : authState.creds.me?.id - ) - ignoreJid = !isNodeFromMe || isJidGroup(attrs.from) ? attrs.from : attrs.recipient - } + try { + const from = node.attrs.from + let ignoreJid = from + if (type === 'receipt' && from) { + const attrs = node.attrs + const isLid = attrs.from!.includes('lid') + const isNodeFromMe = areJidsSameUser( + attrs.participant || attrs.from, + isLid ? authState.creds.me?.lid : authState.creds.me?.id + ) + ignoreJid = !isNodeFromMe || isJidGroup(attrs.from) ? attrs.from : attrs.recipient + } - if (ignoreJid && ignoreJid !== S_WHATSAPP_NET && shouldIgnoreJid(ignoreJid)) { - await sendMessageAck(node, type === 'message' ? NACK_REASONS.UnhandledError : undefined) + if (ignoreJid && ignoreJid !== S_WHATSAPP_NET && shouldIgnoreJid(ignoreJid)) { + // Plain ack (no NACK error code) preserves InfiniteAPI's prior + // behavior — ignored stanzas are an intentional drop, not a + // processing failure, so we don't want server-side retries. + await sendMessageAck(node) + return + } + } catch (error) { + onUnexpectedError(error as Error, identifier) return } }