sipeed/picoclaw · warning · channels.ErrTemporary

whatsapp connection not established: %w

Error message

whatsapp connection not established: %w

What it means

Returned by WhatsAppNativeChannel.Send when the whatsmeow client is nil (channel not started) or client.IsConnected() is false (socket dropped). It wraps channels.ErrTemporary, so the channel manager schedules an exponential-backoff retry instead of dead-lettering the message.

Source

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

	c.HandleInboundContext(c.runCtx, chatID, content, mediaPaths, inboundCtx, sender)
}

func (c *WhatsAppNativeChannel) Send(ctx context.Context, msg bus.OutboundMessage) ([]string, error) {
	if !c.IsRunning() {
		return nil, channels.ErrNotRunning
	}
	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 {

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Check the channel's running/connected state before sending, or rely on the manager's retry.
  2. Restore network connectivity; whatsmeow reconnect paths will flip IsConnected() true again.
  3. Inspect logs for preceding disconnect events to learn why the socket dropped.
  4. If permanent (channel intentionally stopped), pause the channel config so queued sends stop.
Defensive patterns

Strategy: retry

Validate before calling

if !ch.IsRunning() {
    return errors.New("whatsapp native channel not running")
}

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) {
        // not connected yet: backoff; whatsmeow reconnect or Start will restore
    }
}

Prevention

When it happens

Trigger: Send() invoked before Start completes, after Stop, or while whatsmeow is disconnected - e.g. the websocket dropped due to network loss and the event handler's reconnect logic has not yet re-established IsConnected().

Common situations: Outbound message queued while the WhatsApp session is down; transient internet outage on the host; the device re-connecting after sleep; sends racing a restart.

Related errors


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