fix: prevent auto-reconnect during QR pairing phase - #192
Conversation
Reviewer's GuidePrevents routine disconnects during QR pairing from racing the QR rotation lifecycle and resetting the client, while preserving automatic recovery for already-paired sessions. Sequence diagram for pairing-aware disconnect handlingsequenceDiagram
participant WA as WhatsApp
participant Handler as myEventHandler
participant QR as QR rotation lifecycle
participant Service as ReconnectClient
WA->>Handler: Disconnected
alt Store.ID is nil
Handler->>QR: Leave restart lifecycle unchanged
QR->>QR: Rotate QR on 60s/20s timer
else Store.ID is set
Handler->>Service: ReconnectClient(instanceID)
Service->>Service: Restart paired client
end
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 1 issue
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="pkg/whatsmeow/service/whatsmeow.go" line_range="2000-2009" />
<code_context>
+ // this on Store.ID keeps the auto-heal for a real mid-session drop
+ // (the case this was written for) without it also firing on every
+ // pairing-phase blip.
+ if mycli.WAClient != nil && mycli.WAClient.Store.ID != nil {
+ go func(instanceID string) {
+ mycli.loggerWrapper.GetLogger(instanceID).LogInfo("[%s] Disconnected detected, restarting instance", instanceID)
+ if err := mycli.service.ReconnectClient(instanceID); err != nil {
+ mycli.loggerWrapper.GetLogger(instanceID).LogError("[%s] Failed to restart instance: %v", instanceID, err)
+ }
+ }(mycli.userID)
+ } else {
+ mycli.loggerWrapper.GetLogger(mycli.userID).LogInfo("[%s] Disconnected during QR pairing -- leaving restart to the QR-rotation lifecycle, not auto-reconnecting", mycli.userID)
+ }
case *events.LabelEdit:
doWebhook = true
</code_context>
<issue_to_address>
**issue (broader_impact):** When a new-device client emits `*events.Disconnected` while `Store.ID` is nil, this branch does not reconnect it. `StartClient` has disabled `EnableAutoReconnect`, and the QR rotation goroutine only waits, publishes the next QR, and eventually signals teardown; it never calls `Connect`, so a routine socket closure leaves the pairing socket dead until the QR timeout/restart.
**Triggers:** When WhatsApp closes the websocket during QR pairing before the device has been paired.
**Suggested fix:** Have the QR-pairing lifecycle explicitly reconnect the existing client after a transient disconnect, or ensure the client remains/reconnects connected while QR rotation is active; do not simply suppress `ReconnectClient` without replacing its connection-recovery behavior.
</issue_to_address>Sourcery assessment
Approval pending. 1 finding to address first.
Blocking findings: pkg/whatsmeow/service/whatsmeow.go:2009
d3761f4 to
e8b782a
Compare
|
Good catch, thanks — that was a real gap. Pushed an update (force-pushed the same branch, per the contributing guide): a genuinely dropped socket during pairing now gets an explicit reconnect too, it's just a lighter-weight one than before. Instead of calling Guarded with |
Description
*events.DisconnectedinmyEventHandlerunconditionally spawns a goroutine that callsReconnectClient(), regardless of whether the device is still mid-pairing (Store.ID == nil) or already logged in.During new-device QR pairing,
Disconnectedfires routinely and repeatedly — WhatsApp's servers cycle the socket several times before a device is actually paired — which is expected and already handled correctly byhandleQRCodes/teardownQR's own rotation timer (60s for the first code, 20s for each of the rest, 5-code max ≈ 140s total).The problem is that
ReconnectClient()also fires on every one of those routine pairing-phase disconnects, concurrently with the QR-rotation goroutine that's already running. Both paths touch the same unsynchronizedqrcodeCount/ instance maps, andReconnectClient()unconditionally tears the client down and restarts it from scratch ("No jid found. Creating new device"). In practice this collapses the intended ~140s pairing window down to a few seconds — the max QR count gets hit almost immediately, well before a code can realistically be scanned, and the whole cycle repeats indefinitely.Related Issue
None filed yet — happy to open one and link it if preferred.
Root cause (reproduced)
Captured logs from a real pairing attempt show the QR counter reaching its max (5) and forcing a logout/restart within a few seconds of
StartClient, instead of the ~140s the rotation timer implies:Fix
Only auto-reconnect on
*events.Disconnectedonce the device is already paired (mycli.WAClient.Store.ID != nil). A disconnect that happens while still mid-QR-pairing is left entirely to the QR-rotation lifecycle that already owns it — this preserves the auto-heal behavior for the case it looks like it was written for (a real, already-paired session dropping), without it also firing on every pairing-phase blip.After the fix, the same pairing attempt rotates cleanly on the documented schedule with no spurious restarts:
Type of Change
Testing
Verified against a real pairing attempt (scanned QR successfully,
Store.IDset) and a real message send/delivery/read-receipt afterward, confirming the already-paired auto-reconnect path is unaffected.gofmt-clean diff, no other files touched.Additional Notes
Found while integrating Evolution Go's WhatsApp gateway into a separate application — QR pairing was effectively non-functional before this fix (every attempt hit the race within a few seconds of the first code). Happy to adjust the approach if there's a preferred way to guard this instead.
Summary by Sourcery
Keep QR pairing stable by separating pairing-phase recovery from post-pairing session reconnection.
Bug Fixes:
Enhancements: