Exponential Backoff for Reconnections in wacli: Implementation and Usage

wacli uses an exponential backoff strategy in ReconnectWithBackoff to gradually increase delay times between reconnection attempts, respecting minimum and maximum bounds while checking for context cancellation.

The steipete/wacli repository implements a robust reconnection mechanism for WhatsApp Web clients that avoids aggressive retry loops. This article examines the exponential backoff for reconnections logic found in the source code, explaining how the algorithm balances reliability with resource efficiency.

How wacli Implements Exponential Backoff

The core reconnection logic resides in internal/wa/client.go. The Client struct provides the ReconnectWithBackoff method, which orchestrates the retry loop with configurable delay parameters.

The ReconnectWithBackoff Method

Located at lines 52-71 in internal/wa/client.go, this method accepts a context and duration bounds, then executes a controlled retry loop:

func (c *Client) ReconnectWithBackoff(ctx context.Context, minDelay, maxDelay time.Duration) error {
    delay := minDelay
    for {
        if ctx.Err() != nil {
            return ctx.Err()
        }
        if err := c.Connect(ctx, ConnectOptions{AllowQR: false}); err == nil {
            return nil
        }
        select {
        case <-ctx.Done():
            return ctx.Err()
        case <-time.After(delay):
        }
        delay *= 2
        if delay > maxDelay {
            delay = maxDelay
        }
    }
}

The algorithm follows this sequence:

  1. Initialize delay to minDelay.
  2. Check context cancellation before each attempt using ctx.Err().
  3. Attempt connection via c.Connect with AllowQR: false to prevent new QR code generation during reconnection.
  4. Wait for the current delay using time.After(delay) within a select block that also monitors context cancellation.
  5. Double the delay for the next iteration (delay *= 2).
  6. Clamp to maximum if the doubled value exceeds maxDelay.

Integration with the Sync Engine

The sync subsystem in internal/app/sync.go demonstrates practical application of the backoff logic. The App.reconnect method wraps the core functionality with additional timeout controls.

Practical Usage in App.reconnect

At lines 21-32 in internal/app/sync.go, the method invokes the backoff with specific timing constraints:

func (a *App) reconnect(ctx context.Context) error {
    rctx := ctx
    if a.maxReconnect > 0 {
        var cancel context.CancelFunc
        rctx, cancel = context.WithTimeout(ctx, a.maxReconnect)
        defer cancel()
    }
    
    return a.wa.ReconnectWithBackoff(rctx, 2*time.Second, 30*time.Second)
}

This implementation:

  • Uses a minimum delay of 2 seconds to avoid immediate retry storms.
  • Sets a maximum delay of 30 seconds to ensure reconnection attempts occur at least every half minute.
  • Optionally applies an overall timeout via maxReconnect flag, creating a deadline context that terminates the backoff loop after the specified duration.

Complete Usage Example

When building applications with wacli, you can implement custom reconnection logic using the exposed method. The following example demonstrates a five-minute reconnection window with custom delay bounds:

package main

import (
    "context"
    "log"
    "time"
    
    "github.com/steipete/wacli/internal/wa"
)

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
    defer cancel()
    
    client := wa.NewClient()
    
    // Exponential backoff: start at 1s, cap at 10s
    if err := client.ReconnectWithBackoff(ctx, 1*time.Second, 10*time.Second); err != nil {
        log.Fatalf("reconnection failed: %v", err)
    }
    
    log.Println("successfully reconnected")
}

This pattern mirrors the implementation in internal/app/sync.go, allowing you to adjust minDelay, maxDelay, or the parent context duration based on your infrastructure requirements.

Summary

  • wacli implements exponential backoff via ReconnectWithBackoff in internal/wa/client.go to prevent aggressive reconnection attempts.
  • The algorithm doubles the delay after each failed attempt, clamping at a configurable maximum while respecting context cancellation.
  • Default timing uses 2-second minimum and 30-second maximum delays as configured in internal/app/sync.go.
  • The method integrates with Go's context package to support timeout and cancellation propagation throughout the reconnection lifecycle.

Frequently Asked Questions

What is exponential backoff?

Exponential backoff is a retry strategy that progressively increases the wait time between consecutive failed operations. In wacli, this means each reconnection attempt waits twice as long as the previous one, starting from minDelay and capping at maxDelay. This approach reduces server load and network congestion during outages.

How does wacli handle context cancellation during reconnection?

The ReconnectWithBackoff function checks ctx.Err() at the start of each loop iteration and includes a select statement that monitors ctx.Done() during the delay phase. If the context is cancelled or times out, the function immediately returns the context error, terminating the backoff loop without further connection attempts.

What are the default delay values in wacli?

According to the implementation in internal/app/sync.go, the default minimum delay is 2 seconds and the maximum delay is 30 seconds. These values provide a reasonable balance between quick reconnection during transient failures and conservative backoff during extended outages.

Can I customize the backoff parameters?

Yes, the ReconnectWithBackoff method exposes minDelay and maxDelay as parameters, allowing you to specify custom durations based on your use case. Additionally, you can control the overall reconnection timeout by wrapping the context with context.WithTimeout before passing it to the method, as demonstrated in the sync engine implementation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →