sipeed/picoclaw · error · channels.ErrTemporary
whatsapp send: %w
Error message
whatsapp send: %w
What it means
Returned by WhatsAppNativeChannel.Send when whatsmeow's client.SendMessage(ctx, to, waMsg) fails - the message was handed to the WhatsApp protocol stack and the server/link rejected or dropped it. The specific whatsmeow error is discarded and channels.ErrTemporary is wrapped, so the manager retries with backoff. Causes range from network drops mid-send to pairing/session-key problems and server-side rejections.
Source
Thrown at pkg/channels/whatsapp_native/whatsapp_native.go:444
}
// 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.
func parseJID(s string) (types.JID, error) {
s = strings.TrimSpace(s)
if s == "" {
return types.JID{}, fmt.Errorf("empty chat id")
}
if strings.Contains(s, "@") {
return types.ParseJID(s)
}
return types.NewJID(s, types.DefaultUserServer), nil
}
View on GitHub (pinned to 49183d7e8d)
Solutions
- Check recent logs for disconnect/pairing events - if the device was logged out, delete the store, restart, and re-scan QR.
- Verify the recipient number actually has a WhatsApp account (test from the phone app).
- If errors cluster with network instability, wait for reconnect; backoff retry will resend.
- For permanent-looking rejections (e.g. banned number), stop retrying by moving the message to a dead-letter path.
Defensive patterns
Strategy: retry
Validate before calling
if !ch.IsRunning() { return errors.New("channel not running") }
// Pairing/connect gates are internal; ensure no recent 'logged out' events before sending Type guard
func isTemporary(err error) bool { return errors.Is(err, channels.ErrTemporary) } Try / catch
if _, err := ch.Send(ctx, msg); err != nil {
if errors.Is(err, channels.ErrTemporary) {
// backoff retry; if repeated, suspect session invalidation -> re-pair
}
} Prevention
- Do not unlink the device from the phone while the channel is in use.
- Cap retries for messages whose recipient may not be on WhatsApp.
- Watch for repeat ErrTemporary on the same chat id as a signal of a permanent recipient problem.
When it happens
Trigger: Send() on a connected+paired client where SendMessage errors: socket drops between the IsConnected check and the write, session keys out of sync after phone-side logout, recipient JID not registered on WhatsApp, or WhatsApp server rejecting the payload.
Common situations: User unlinked the device from the phone (session invalid but socket briefly still up); phone offline for extended period causing key issues; sending to numbers that never had WhatsApp; transient server errors.
Related errors
- whatsapp connection not established: %w
- whatsapp connection not established: %w
- whatsapp send: %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/0002056a2f31e8ca.
Report an issue: GitHub.