Telego Long Polling Retry Timeout: Configuration and Best Practices

The WithLongPollingRetryTimeout option in Telego controls how long the library waits before retrying failed GetUpdates requests during long polling, defaulting to 8 seconds and disabling automatic retries when explicitly set to zero.

Telego, the popular Go library for the Telegram Bot API available at mymmrac/telego, provides resilient long polling capabilities through granular configuration options. Understanding the Telego long polling retry timeout mechanism is essential for building bots that gracefully handle network interruptions without overwhelming Telegram's servers or your own infrastructure.

What Is the Long Polling Retry Timeout?

The WithLongPollingRetryTimeout configuration option defines the delay between retry attempts when the GetUpdates method fails during long polling operations. This mechanism prevents aggressive retry storms while ensuring your bot recovers automatically from transient network failures.

According to the source code in long_polling.go, this value is stored in the retryTimeout field of the internal longPolling struct and is evaluated within the doLongPolling loop after each failed request.

Default Behavior and Configuration

Default Value (8 Seconds)

By default, Telego applies an 8-second retry timeout defined by the constant defaultLongPollingRetryTimeout. This conservative delay balances quick recovery from transient errors with respectful API usage.

lp := &longPolling{
    updateChanBuffer: defaultLongPollingUpdateChanBuffer,
    updateInterval:   defaultLongPollingUpdateInterval,
    retryTimeout:     defaultLongPollingRetryTimeout, // 8 seconds
}

Source: long_polling.go lines 58-62

Zero Value Behavior (Disables Retries)

Setting the retry timeout to 0 completely disables automatic retries. When GetUpdates encounters its first error, the update channel closes immediately and your bot stops polling.

updates, err := bot.UpdatesViaLongPolling(
    ctx,
    nil,
    telego.WithLongPollingRetryTimeout(0), // disable retries
)

This mode is useful when you prefer to handle errors explicitly through external monitoring rather than automatic recovery.

Negative Values (Validation Errors)

Telego validates the retry timeout during option processing. Passing a negative duration results in an immediate error: "retry timeout is negative".

func WithLongPollingRetryTimeout(retryTimeout time.Duration) LongPollingOption {
    return func(lp *longPolling) error {
        if retryTimeout < 0 {
            return fmt.Errorf("retry timeout is negative: %s", retryTimeout)
        }
        lp.retryTimeout = retryTimeout
        return nil
    }
}

Source: long_polling.go lines 45-55

How the Retry Timeout Works Internally

The retry mechanism operates within the doLongPolling function in long_polling.go. When a GetUpdates request fails, the logic evaluates the configured retryTimeout:

if lp.retryTimeout == 0 || errors.Is(err, context.Canceled) {
    return
}
b.log.Errorf("Retrying getting updates in %s...", lp.retryTimeout.String())
time.Sleep(lp.retryTimeout)
continue

Source: long_polling.go lines 27-33

This implementation ensures that:

  • Context cancellation always exits immediately regardless of timeout settings
  • Zero timeout prevents any retry attempts
  • Positive timeouts trigger a sleep delay before the next request

Practical Code Examples

Using the Default 8-Second Timeout

The simplest implementation relies on Telego's built-in resilience:

bot, _ := telego.NewBot("YOUR_TOKEN")
ctx := context.Background()

updates, _ := bot.UpdatesViaLongPolling(ctx, nil) // default 8s retry timeout
for update := range updates {
    // Process update
}

Customizing the Retry Duration

For production environments with specific SLA requirements, specify a custom duration:

updates, err := bot.UpdatesViaLongPolling(
    ctx,
    nil,
    telego.WithLongPollingRetryTimeout(5*time.Second), // 5 second retry
)
if err != nil {
    log.Fatal(err)
}

Disabling Retries Entirely

For debugging or when implementing custom error handling at the application level:

updates, err := bot.UpdatesViaLongPolling(
    ctx,
    nil,
    telego.WithLongPollingRetryTimeout(0), // disable automatic retries
)
if err != nil {
    log.Fatal(err)
}

// Channel closes immediately on first error
for upd := range updates {
    processUpdate(upd)
}
// Execution continues here after channel closes due to error

Handling Channel Closure

Always implement proper channel handling to detect when retries are exhausted or disabled:

for {
    select {
    case upd, ok := <-updates:
        if !ok {
            log.Println("Update channel closed, exiting")
            return
        }
        handleUpdate(upd)
    case <-ctx.Done():
        return
    }
}

Interaction with Other Long Polling Options

The retry timeout operates independently from other configuration options:

  • WithLongPollingUpdateInterval: Adds a fixed pause between successful polls, while WithLongPollingRetryTimeout controls delays after failures
  • WithLongPollingBuffer: Defines the channel buffer size for incoming updates, unaffected by retry logic

Understanding these distinctions ensures you configure the polling loop holistically rather than treating options in isolation.

Summary

  • WithLongPollingRetryTimeout controls the delay between retry attempts when GetUpdates fails during Telego long polling
  • The default value is 8 seconds, defined by defaultLongPollingRetryTimeout in long_polling.go
  • Setting the timeout to zero disables automatic retries, causing the update channel to close on the first error
  • Negative values return validation errors during option processing
  • The retry logic executes in the doLongPolling function, sleeping for the specified duration before subsequent requests
  • This option works independently from WithLongPollingUpdateInterval (success delays) and WithLongPollingBuffer (channel sizing)

Frequently Asked Questions

What happens when Telego long polling retry timeout is set to zero?

When you pass telego.WithLongPollingRetryTimeout(0), Telego disables automatic retries entirely. If the GetUpdates request fails for any reason other than context cancellation, the internal doLongPolling loop returns immediately, triggering deferred cleanup that closes the updates channel. Your application can detect this closure to implement custom error handling or shutdown procedures.

How does Telego long polling retry timeout interact with update intervals?

The WithLongPollingRetryTimeout and WithLongPollingUpdateInterval options serve different phases of the polling cycle. The update interval adds a fixed sleep duration between successful GetUpdates requests to prevent aggressive polling. The retry timeout only activates after failed requests, controlling how long Telego waits before attempting recovery. These durations are not additive; they apply to mutually exclusive states in the polling loop.

Can I set a negative retry timeout in Telego?

No, Telego explicitly rejects negative durations during option processing. The WithLongPollingRetryTimeout function validates the input and returns fmt.Errorf("retry timeout is negative: %s", retryTimeout) if you pass a value less than zero. This validation occurs before the long polling starts, ensuring that configuration errors surface immediately rather than during runtime execution.

Where is the retry logic implemented in Telego?

The retry mechanism resides in the doLongPolling function within long_polling.go. After a failed GetUpdates call, the code checks lp.retryTimeout at lines 27-33. If the timeout is positive, the function logs the retry attempt, sleeps for the specified duration using time.Sleep(lp.retryTimeout), and continues the loop. If the timeout is zero or the error is context.Canceled, the function returns and closes the updates channel.

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 →