sipeed/picoclaw · error · channels.ErrTemporary

whatsapp not yet paired (QR login pending): %w

Error message

whatsapp not yet paired (QR login pending): %w

What it means

Returned by WhatsAppNativeChannel.Send when the whatsmeow client IS connected to WhatsApp servers but client.Store.ID is nil - the device has not completed QR pairing, so it has no WhatsApp identity and cannot send messages. Wraps channels.ErrTemporary, so the manager keeps retrying with backoff, but retries cannot succeed until a human scans the QR code.

Source

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

	}
	select {
	case <-ctx.Done():
		return nil, ctx.Err()
	default:
	}

	c.mu.Lock()
	client := c.client
	c.mu.Unlock()

	if client == nil || !client.IsConnected() {
		return nil, fmt.Errorf("whatsapp connection not established: %w", channels.ErrTemporary)
	}

	// Detect unpaired state: the client is connected (to WhatsApp servers)
	// but has not completed QR-login yet, so sending would fail.
	if client.Store.ID == nil {
		return nil, fmt.Errorf("whatsapp not yet paired (QR login pending): %w", channels.ErrTemporary)
	}

	to, err := parseJID(msg.ChatID)
	if err != nil {
		return nil, fmt.Errorf("invalid chat id %q: %w", msg.ChatID, err)
	}

	waMsg := &waE2E.Message{
		Conversation: proto.String(msg.Content),
	}

	if _, err = client.SendMessage(ctx, to, waMsg); err != nil {
		return nil, fmt.Errorf("whatsapp send: %w", channels.ErrTemporary)
	}
	return nil, nil
}

// parseJID converts a chat ID (phone number or JID string) to types.JID.

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Watch the process stdout/console for the rendered QR (qrterminal) and scan it from the WhatsApp phone app: Linked devices > Link a device.
  2. Keep the process running - pairing only progresses while the channel is up.
  3. After the 'success' QR event, retry sending (manager backoff will, or trigger manually).
  4. If the QR keeps expiring unscanned, reduce the delay between Start and scanning.
Defensive patterns

Strategy: validation

Validate before calling

// Do not route outbound traffic until the device is paired.
// If the channel exposes pairing state (client.Store.ID != nil), gate sends on it;
// otherwise delay sends until the 'success' QR event appears in logs.

Type guard

func isTemporary(err error) bool { return errors.Is(err, channels.ErrTemporary) }

Try / catch

if _, err := ch.Send(ctx, msg); err != nil {
    if strings.Contains(err.Error(), "not yet paired") {
        // surface to operator: scan the QR code; retry send after login succeeds
    }
}

Prevention

When it happens

Trigger: Fresh install (new store) where Start() launched the QR flow but nobody has scanned it yet; a re-pair after store deletion; scans pending while outbound traffic is already routed to the channel.

Common situations: Operator deploys the native WhatsApp channel and sends a test message before completing the console QR login; the QR code expired (whatsmeow refreshes it, but pairing still pending); tests in CI without any paired device.

Related errors


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