sipeed/picoclaw · error
get QR channel: %w
Error message
get QR channel: %w
What it means
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.
Source
Thrown at pkg/channels/whatsapp_native/whatsapp_native.go:162
// of a partially-initialized client, or stray event handler callbacks).
startOK := false
defer func() {
if startOK {
return
}
c.runCancel()
client.Disconnect()
c.mu.Lock()
c.client = nil
c.container = nil
c.mu.Unlock()
_ = container.Close()
}()
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() {View on GitHub (pinned to 49183d7e8d)
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.
Defensive patterns
Strategy: validation
Validate before calling
// Serialize lifecycle: only Start when not already started/stopping.
if ch.IsRunning() { return errors.New("channel already running") }
// and always Stop() to completion before the next Start Try / catch
if err := ch.Start(ctx); err != nil {
if strings.Contains(err.Error(), "get QR channel") {
// a previous client is likely still connected; Stop fully, wait, retry Start once
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- connect: %w
- channel stopped during QR setup
- read media file: %w
- create session store dir: %w
- get device store: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/d74c4da87d8e3f6f.
Report an issue: GitHub.