{"record":{"id":"d74c4da87d8e3f6f","repo":"sipeed/picoclaw","slug":"get-qr-channel-w","errorCode":null,"errorMessage":"get QR channel: %w","messagePattern":"get QR channel: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/channels/whatsapp_native/whatsapp_native.go","lineNumber":162,"sourceCode":"\t// of a partially-initialized client, or stray event handler callbacks).\n\tstartOK := false\n\tdefer func() {\n\t\tif startOK {\n\t\t\treturn\n\t\t}\n\t\tc.runCancel()\n\t\tclient.Disconnect()\n\t\tc.mu.Lock()\n\t\tc.client = nil\n\t\tc.container = nil\n\t\tc.mu.Unlock()\n\t\t_ = container.Close()\n\t}()\n\n\tif client.Store.ID == nil {\n\t\tqrChan, err := client.GetQRChannel(c.runCtx)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"get QR channel: %w\", err)\n\t\t}\n\t\tif err := client.Connect(); err != nil {\n\t\t\treturn fmt.Errorf(\"connect: %w\", err)\n\t\t}\n\t\t// Handle QR events in a background goroutine so Start() returns\n\t\t// promptly.  The goroutine is tracked via c.wg and respects\n\t\t// c.runCtx for cancellation.\n\t\t// Guard wg.Add with reconnectMu + stopping check (same protocol\n\t\t// as eventHandler) so a concurrent Stop() cannot enter wg.Wait()\n\t\t// while we call wg.Add(1).\n\t\tc.reconnectMu.Lock()\n\t\tif c.stopping.Load() {\n\t\t\tc.reconnectMu.Unlock()\n\t\t\treturn fmt.Errorf(\"channel stopped during QR setup\")\n\t\t}\n\t\tc.wg.Add(1)\n\t\tc.reconnectMu.Unlock()\n\t\tgo func() {","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/whatsapp_native/whatsapp_native.go#L144-L180","documentation":"Returned by WhatsAppNativeChannel.Start when whatsmeow's client.GetQRChannel(runCtx) fails before any connection is made. whatsmeow refuses to set up a QR channel if the client socket is already connected, or if runCtx is already cancelled. The deferred cleanup disconnects the client and closes the container.","triggerScenarios":"Start() on an unpaired device (Store.ID == nil) while a previous Start's client is still connected, or Stop() cancelled runCtx concurrently, or calling Start twice in quick succession.","commonSituations":"Restart races: operator restarts the channel while an earlier Start is mid-flow; supervision loop retrying Start too aggressively; accidental double-registration of the same channel.","solutions":["Ensure Stop() fully completes (it waits via c.wg) before calling Start() again.","Serialize Start/Stop through the channel manager rather than calling them directly from your own code.","If it persists, check that no second goroutine is driving the same WhatsAppNativeChannel.","Add a small delay/backoff in your supervision loop before re-Start."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Serialize lifecycle: only Start when not already started/stopping.\nif ch.IsRunning() { return errors.New(\"channel already running\") }\n// and always Stop() to completion before the next Start","typeGuard":null,"tryCatchPattern":"if err := ch.Start(ctx); err != nil {\n    if strings.Contains(err.Error(), \"get QR channel\") {\n        // a previous client is likely still connected; Stop fully, wait, retry Start once\n    }\n}","preventionTips":["Never call Start concurrently from two goroutines.","Route lifecycle through the channel manager which serializes transitions.","In supervision loops, add jitter/backoff before re-Start attempts."],"tags":["whatsapp","whatsmeow","qr-login","race","lifecycle"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}