fix(signal): critical fixes for identity key detection - batch 1
High Priority Fixes based on Copilot/Codex code review: 1. Propagate errorCode to shouldRecreateSession (CRITICAL) - Extract error attribute from retry node - Pass errorCode to shouldRecreateSession in both sendRetryRequest and sendMessagesAgain - Without this fix, MAC error detection was NOT working - Now immediate session recreation works for MAC errors 2. Use .unref() on metrics interval - Prevents blocking process exit in short-lived scripts/tests - Interval no longer keeps event loop alive unnecessarily 3. Reset circuit breaker regardless of state - Previously only reset when isOpen() - Now resets in any state (open, half-open, or closed with accumulated failures) - Ensures clean slate after identity change detection https://claude.ai/code/session_01SWAcNuGZQmEKyBPYkBhVHg
This commit is contained in:
@@ -255,9 +255,9 @@ export function makeLibSignalRepository(
|
|||||||
metrics.signalIdentityKeyCacheSize?.set(identityKeyCache.size)
|
metrics.signalIdentityKeyCacheSize?.set(identityKeyCache.size)
|
||||||
}, 60000) // Every minute
|
}, 60000) // Every minute
|
||||||
|
|
||||||
// Cleanup interval on process exit
|
// Allow process to exit even with interval active (prevents blocking in short-lived scripts/tests)
|
||||||
if (typeof process !== 'undefined') {
|
if (typeof cacheMetricsInterval.unref === 'function') {
|
||||||
process.on('beforeExit', () => clearInterval(cacheMetricsInterval))
|
cacheMetricsInterval.unref()
|
||||||
}
|
}
|
||||||
|
|
||||||
const storage = signalStorage(auth, lidMapping, identityKeyCache, ev, preKeyCircuitBreaker, logger)
|
const storage = signalStorage(auth, lidMapping, identityKeyCache, ev, preKeyCircuitBreaker, logger)
|
||||||
@@ -334,7 +334,8 @@ export function makeLibSignalRepository(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Reset prekey circuit breaker since we identified the cause
|
// Reset prekey circuit breaker since we identified the cause
|
||||||
if (preKeyCircuitBreaker?.isOpen()) {
|
// Reset regardless of state (could be open, half-open, or closed with accumulated failures)
|
||||||
|
if (preKeyCircuitBreaker) {
|
||||||
preKeyCircuitBreaker.reset()
|
preKeyCircuitBreaker.reset()
|
||||||
logger.debug({ jid }, 'Reset prekey circuit breaker after identity key change detection')
|
logger.debug({ jid }, 'Reset prekey circuit breaker after identity key change detection')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -452,12 +452,18 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
|||||||
// Check if we have a session with this JID
|
// Check if we have a session with this JID
|
||||||
const sessionId = signalRepository.jidToSignalProtocolAddress(fromJid)
|
const sessionId = signalRepository.jidToSignalProtocolAddress(fromJid)
|
||||||
const hasSession = await signalRepository.validateSession(fromJid)
|
const hasSession = await signalRepository.validateSession(fromJid)
|
||||||
const result = messageRetryManager.shouldRecreateSession(fromJid, hasSession.exists)
|
|
||||||
|
// Extract error code from retry node if present (for MAC error detection)
|
||||||
|
const retryNode = getBinaryNodeChild(node, 'retry')
|
||||||
|
const errorAttr = retryNode?.attrs?.error as string | undefined
|
||||||
|
const errorCode = messageRetryManager.parseRetryErrorCode(errorAttr)
|
||||||
|
|
||||||
|
const result = messageRetryManager.shouldRecreateSession(fromJid, hasSession.exists, errorCode)
|
||||||
shouldRecreateSession = result.recreate
|
shouldRecreateSession = result.recreate
|
||||||
recreateReason = result.reason
|
recreateReason = result.reason
|
||||||
|
|
||||||
if (shouldRecreateSession) {
|
if (shouldRecreateSession) {
|
||||||
logger.debug({ fromJid, retryCount, reason: recreateReason }, 'recreating session for retry')
|
logger.debug({ fromJid, retryCount, reason: recreateReason, errorCode }, 'recreating session for retry')
|
||||||
// Delete existing session to force recreation
|
// Delete existing session to force recreation
|
||||||
await authState.keys.set({ session: { [sessionId]: null } })
|
await authState.keys.set({ session: { [sessionId]: null } })
|
||||||
forceIncludeKeys = true
|
forceIncludeKeys = true
|
||||||
@@ -1006,12 +1012,17 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
|||||||
const sessionId = signalRepository.jidToSignalProtocolAddress(participant)
|
const sessionId = signalRepository.jidToSignalProtocolAddress(participant)
|
||||||
|
|
||||||
const hasSession = await signalRepository.validateSession(participant)
|
const hasSession = await signalRepository.validateSession(participant)
|
||||||
const result = messageRetryManager.shouldRecreateSession(participant, hasSession.exists)
|
|
||||||
|
// Extract error code from retry node if present (for MAC error detection)
|
||||||
|
const errorAttr = retryNode?.attrs?.error as string | undefined
|
||||||
|
const errorCode = messageRetryManager.parseRetryErrorCode(errorAttr)
|
||||||
|
|
||||||
|
const result = messageRetryManager.shouldRecreateSession(participant, hasSession.exists, errorCode)
|
||||||
shouldRecreateSession = result.recreate
|
shouldRecreateSession = result.recreate
|
||||||
recreateReason = result.reason
|
recreateReason = result.reason
|
||||||
|
|
||||||
if (shouldRecreateSession) {
|
if (shouldRecreateSession) {
|
||||||
logger.debug({ participant, retryCount, reason: recreateReason }, 'recreating session for outgoing retry')
|
logger.debug({ participant, retryCount, reason: recreateReason, errorCode }, 'recreating session for outgoing retry')
|
||||||
await authState.keys.set({ session: { [sessionId]: null } })
|
await authState.keys.set({ session: { [sessionId]: null } })
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user