sipeed/picoclaw · critical
failed to connect to WhatsApp bridge: %w
Error message
failed to connect to WhatsApp bridge: %w
What it means
Returned by WhatsAppChannel.Start when gorilla/websocket's Dialer.Dial fails to establish a connection to the configured bridge URL (cfg.BridgeURL). The %w chain preserves the underlying error, commonly websocket.ErrBadHandshake when the server replies with a non-101 status. The channel's internal context is cancelled before returning, so the channel is left stopped.
Source
Thrown at pkg/channels/whatsapp/whatsapp.go:69
}
func (c *WhatsAppChannel) Start(ctx context.Context) error {
logger.InfoCF("whatsapp", "Starting WhatsApp channel", map[string]any{
"bridge_url": c.url,
})
c.ctx, c.cancel = context.WithCancel(ctx)
dialer := websocket.DefaultDialer
dialer.HandshakeTimeout = 10 * time.Second
conn, resp, err := dialer.Dial(c.url, nil)
if resp != nil {
_ = resp.Body.Close()
}
if err != nil {
c.cancel()
return fmt.Errorf("failed to connect to WhatsApp bridge: %w", err)
}
c.mu.Lock()
c.conn = conn
c.connected = true
c.mu.Unlock()
c.SetRunning(true)
logger.InfoC("whatsapp", "WhatsApp channel connected")
go c.listen()
return nil
}
func (c *WhatsAppChannel) Stop(ctx context.Context) error {
logger.InfoC("whatsapp", "Stopping WhatsApp channel...")
View on GitHub (pinned to 49183d7e8d)
Solutions
- Verify the bridge process is running and its port matches config.BridgeURL (e.g. ws://127.0.0.1:PORT).
- Test reachability with a standalone websocket client or curl against the bridge's HTTP health endpoint.
- If a reverse proxy fronts the bridge, add websocket upgrade headers (Upgrade/Connection) to that route.
- For wss:// certificate failures, fix the cert chain or point to a properly signed cert.
- Ensure the URL scheme is ws:// or wss://, not http:// or https://.
Example fix
// before url := "http://127.0.0.1:8125" // wrong scheme // after url := "ws://127.0.0.1:8125"
Defensive patterns
Strategy: validation
Validate before calling
u, err := url.Parse(cfg.BridgeURL)
if err != nil || (u.Scheme != "ws" && u.Scheme != "wss") {
return fmt.Errorf("bridge URL must be ws:// or wss://, got %q", cfg.BridgeURL)
}
if _, err := net.DialTimeout("tcp", u.Host, 3*time.Second); err != nil {
return fmt.Errorf("bridge unreachable: %w", err)
} Try / catch
if err := ch.Start(ctx); err != nil {
if strings.Contains(err.Error(), "failed to connect to WhatsApp bridge") {
// inspect errors.Unwrap chain for websocket.ErrBadHandshake => HTTP-level problem
}
return err
} Prevention
- Pre-flight the bridge with a TCP dial or health endpoint before starting the channel.
- Use supervision with backoff around Start so bridge outages self-heal.
- Document the required ws:// scheme in channel config templates to prevent http:// typos.
When it happens
Trigger: Start(ctx) calls dialer.Dial(c.url, nil) with a 10s handshake timeout: bridge process not running, wrong scheme (http:// instead of ws://), bridge listening on a different host/port, TLS failure on wss://, or reverse proxy not upgrading the connection.
Common situations: The WhatsApp bridge container/service (e.g. a whatsapp-web bridge) is down or was never started; BridgeURL in channel config points to the wrong port; an nginx/caddy front proxy lacks Upgrade/Connection headers for websocket; self-signed cert on wss:// rejected by default TLS config.
Related errors
- failed to start stream client: %w
- failed to open discord session: %w
- failed to get websocket info: %w
- whatsapp connection not established: %w
- whatsapp send: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/e61b27f154d69f63.
Report an issue: GitHub.