{"record":{"id":"8cbb3e0456ffd9da","repo":"sipeed/picoclaw","slug":"channel-stopped-during-qr-setup","errorCode":null,"errorMessage":"channel stopped during QR setup","messagePattern":"channel stopped during QR setup","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"pkg/channels/whatsapp_native/whatsapp_native.go","lineNumber":176,"sourceCode":"\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() {\n\t\t\tdefer c.wg.Done()\n\t\t\tfor {\n\t\t\t\tselect {\n\t\t\t\tcase <-c.runCtx.Done():\n\t\t\t\t\treturn\n\t\t\t\tcase evt, ok := <-qrChan:\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tif evt.Event == \"code\" {\n\t\t\t\t\t\tlogger.InfoCF(\"whatsapp\", \"Scan this QR code with WhatsApp (Linked Devices):\", nil)\n\t\t\t\t\t\tqrterminal.GenerateWithConfig(evt.Code, qrterminal.Config{\n\t\t\t\t\t\t\tLevel:      qrterminal.L,\n\t\t\t\t\t\t\tWriter:     os.Stdout,","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/whatsapp_native/whatsapp_native.go#L158-L194","documentation":"Returned by WhatsAppNativeChannel.Start when the stopping flag is observed while holding reconnectMu during QR-login setup - i.e. Stop() ran concurrently with Start(). This is a deliberate clean abort, not a fault: the just-connected client is disconnected by the deferred cleanup and Start reports that the channel never came up. Note it is a plain error with no %w sentinel.","triggerScenarios":"Stop(ctx) is called between client.Connect() and the wg.Add(1) for the QR goroutine; the stopping.Store(true) from Stop becomes visible under the reconnectMu lock and Start bails out.","commonSituations":"Rapid restart cycles (supervisor stop+start), config reloads that stop channels mid-start, or shutdown initiated while an unpaired channel is printing QR codes.","solutions":["No repair needed - this is graceful cancellation; simply Start again after Stop completes.","In supervision code, wait for Stop() to return before issuing the next Start().","Treat this message as informational in log alerting rather than an incident."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Ensure Stop has fully returned (it waits on c.wg) before starting again:\nif err := ch.Stop(stopCtx); err != nil { return err }\n// only then:\nreturn ch.Start(ctx)","typeGuard":null,"tryCatchPattern":"if err := ch.Start(ctx); err != nil {\n    if errors.Is(err, context.Canceled) || strings.Contains(err.Error(), \"stopped during QR setup\") {\n        // benign abort: retry Start after lifecycle settles\n    }\n}","preventionTips":["Treat this message as a no-op in alerting.","Serialize Stop->Start sequences in reconfiguration code.","Prefer the manager's restart APIs over manual Stop/Start pairs."],"tags":["whatsapp","lifecycle","race","shutdown","graceful"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}