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

  1. Check recent logs for disconnect/pairing events - if the device was logged out, delete the store, restart, and re-scan QR.
  2. Verify the recipient number actually has a WhatsApp account (test from the phone app).
  3. If errors cluster with network instability, wait for reconnect; backoff retry will resend.
  4. 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

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


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