sipeed/picoclaw · warning · channels.ErrTemporary
whatsapp connection not established: %w
Error message
whatsapp connection not established: %w
What it means
Returned by the WhatsApp bridge channel's Send when c.conn is nil - i.e. Start() never completed a bridge connection or Stop() has torn it down. It wraps channels.ErrTemporary, so the channel manager treats it as transient and retries with backoff rather than dropping the message permanently.
Source
Thrown at pkg/channels/whatsapp/whatsapp.go:127
}
func (c *WhatsAppChannel) Send(ctx context.Context, msg bus.OutboundMessage) ([]string, error) {
if !c.IsRunning() {
return nil, channels.ErrNotRunning
}
// Check ctx before acquiring lock
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
c.mu.Lock()
defer c.mu.Unlock()
if c.conn == nil {
return nil, fmt.Errorf("whatsapp connection not established: %w", channels.ErrTemporary)
}
payload := map[string]any{
"type": "message",
"to": msg.ChatID,
"content": msg.Content,
}
data, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("failed to marshal message: %w", err)
}
_ = c.conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
if err := c.conn.WriteMessage(websocket.TextMessage, data); err != nil {
_ = c.conn.SetWriteDeadline(time.Time{})
return nil, fmt.Errorf("whatsapp send: %w", channels.ErrTemporary)
}View on GitHub (pinned to 49183d7e8d)
Solutions
- Check whether the channel reports running state before send - IsRunning() gate usually prevents this path.
- Fix the underlying bridge connectivity so Start() succeeds (see bridge URL, process, proxy checks).
- Rely on the manager's ErrTemporary backoff: queued outbound messages retry automatically once the bridge is back.
- If it persists, inspect logs for a prior 'failed to connect to WhatsApp bridge' error explaining why conn is nil.
Defensive patterns
Strategy: retry
Validate before calling
if !ch.IsRunning() {
return errors.New("whatsapp bridge 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) {
// conn nil or send failure: retry after bridge reconnects
}
} Prevention
- Gate sends on IsRunning() to avoid sending into a channel whose Start failed.
- Keep the bridge process supervised so conn is re-established quickly.
- Let the channel manager own retry policy instead of ad-hoc retries in callers.
When it happens
Trigger: Send(ctx, msg) is invoked after Start() failed (bridge unreachable), after Stop() released the websocket, or while the channel is between a connection drop and restart.
Common situations: Bridge outage that crashed Start; sending queued messages right after process restart before the channel reconnects; a Stop/Start race during reconfiguration where Send slips in with conn still nil.
Related errors
- whatsapp send: %w
- whatsapp connection not established: %w
- whatsapp send: %w
- failed to connect to WhatsApp bridge: %w
- read error response: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/1af0ca97c83f7215.
Report an issue: GitHub.