sipeed/picoclaw · info

channel stopped during QR setup

Error message

channel stopped during QR setup

What it means

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.

Source

Thrown at pkg/channels/whatsapp_native/whatsapp_native.go:176

	if client.Store.ID == nil {
		qrChan, err := client.GetQRChannel(c.runCtx)
		if err != nil {
			return fmt.Errorf("get QR channel: %w", err)
		}
		if err := client.Connect(); err != nil {
			return fmt.Errorf("connect: %w", err)
		}
		// Handle QR events in a background goroutine so Start() returns
		// promptly.  The goroutine is tracked via c.wg and respects
		// c.runCtx for cancellation.
		// Guard wg.Add with reconnectMu + stopping check (same protocol
		// as eventHandler) so a concurrent Stop() cannot enter wg.Wait()
		// while we call wg.Add(1).
		c.reconnectMu.Lock()
		if c.stopping.Load() {
			c.reconnectMu.Unlock()
			return fmt.Errorf("channel stopped during QR setup")
		}
		c.wg.Add(1)
		c.reconnectMu.Unlock()
		go func() {
			defer c.wg.Done()
			for {
				select {
				case <-c.runCtx.Done():
					return
				case evt, ok := <-qrChan:
					if !ok {
						return
					}
					if evt.Event == "code" {
						logger.InfoCF("whatsapp", "Scan this QR code with WhatsApp (Linked Devices):", nil)
						qrterminal.GenerateWithConfig(evt.Code, qrterminal.Config{
							Level:      qrterminal.L,
							Writer:     os.Stdout,

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. No repair needed - this is graceful cancellation; simply Start again after Stop completes.
  2. In supervision code, wait for Stop() to return before issuing the next Start().
  3. Treat this message as informational in log alerting rather than an incident.
Defensive patterns

Strategy: validation

Validate before calling

// Ensure Stop has fully returned (it waits on c.wg) before starting again:
if err := ch.Stop(stopCtx); err != nil { return err }
// only then:
return ch.Start(ctx)

Try / catch

if err := ch.Start(ctx); err != nil {
    if errors.Is(err, context.Canceled) || strings.Contains(err.Error(), "stopped during QR setup") {
        // benign abort: retry Start after lifecycle settles
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: Rapid restart cycles (supervisor stop+start), config reloads that stop channels mid-start, or shutdown initiated while an unpaired channel is printing QR codes.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/8cbb3e0456ffd9da. Report an issue: GitHub.