sipeed/picoclaw · error · channels.ErrTemporary
whatsapp send: %w
Error message
whatsapp send: %w
What it means
Returned by the WhatsApp bridge channel's Send when conn.WriteMessage fails within a 10-second write deadline. The write deadline is cleared and the raw websocket error is discarded in favour of the channels.ErrTemporary sentinel, so the manager retries the message with exponential backoff. Typical websocket causes: connection already closed by peer, TCP reset, or the peer stalling until the deadline expires.
Source
Thrown at pkg/channels/whatsapp/whatsapp.go:144
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)
}
_ = c.conn.SetWriteDeadline(time.Time{})
return nil, nil
}
func (c *WhatsAppChannel) listen() {
for {
select {
case <-c.ctx.Done():
return
default:
c.mu.Lock()
conn := c.conn
c.mu.Unlock()
if conn == nil {
time.Sleep(1 * time.Second)View on GitHub (pinned to 49183d7e8d)
Solutions
- Verify the bridge process is alive and its logs show no crash/restart loop.
- If a NAT/firewall idles out the socket, enable websocket ping/keepalive on the bridge or dialer so drops are detected proactively.
- Restart the picoclaw channel (Stop/Start) to re-dial the bridge - the manager's backoff will then re-deliver the queued message.
- Check host networking (DNS, egress) if writes fail immediately after dial.
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) {
// write failed or conn lost: backoff and retry; consider Stop/Start of channel if repeated
}
} Prevention
- Enable websocket keepalive/ping on the bridge path so dead peers are detected before writes.
- Alert on repeated ErrTemporary from the whatsapp channel - it usually means the bridge is flapping.
- Restart the channel via Stop/Start when errors persist to force a clean re-dial.
When it happens
Trigger: Send() writes a TextMessage on a websocket whose remote end has disconnected (bridge crash/restart), whose TCP link is broken, or whose read buffer backpressure stalls past the 10s SetWriteDeadline.
Common situations: Bridge restarted under load; NAT/firewall silently dropping the idle websocket (no keepalive/ping configured); mobile/network flaps on the host running the bridge; slow bridge consumer with full receive buffers.
Related errors
- whatsapp connection not established: %w
- failed to connect to WhatsApp bridge: %w
- whatsapp connection not established: %w
- whatsapp send: %w
- read error response: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/677b9f2fca780860.
Report an issue: GitHub.