Skip to content

Sticky IPv4 fallback resets on polling restart, causing repeated IPv6 timeouts #48177

@kiki830621

Description

@kiki830621

Problem

When autoSelectFamily=true (Node 22+ default) and the host has unstable IPv6 connectivity to api.telegram.org, the Telegram transport correctly detects the failure and enables a "sticky IPv4-only dispatcher". However, this sticky state is lost on every polling restart, causing a repeating cycle of IPv6 timeouts.

Root Cause

createTelegramBot() in src/telegram/bot.ts:135 calls resolveTelegramTransport() on every invocation, creating a new fetch closure with stickyIpv4FallbackEnabled = false.

When TelegramPollingSession.runUntilAbort() detects a polling stall and restarts, it calls #createPollingBot()createTelegramBot() → new resolveTelegramTransport() → sticky state reset.

Cycle

1. IPv6 connect timeout (UND_ERR_CONNECT_TIMEOUT)
2. fetch.ts enables stickyIpv4FallbackEnabled = true → works
3. Polling stall detected (~90s no getUpdates)
4. Polling restart → new bot → new transport → sticky reset to false
5. Next fetch tries IPv6 again → timeout
6. Repeat from step 1

Observed Impact

On a host with intermittent IPv6 to Telegram (common with residential ISPs in Asia):

  • 47 polling stalls in 20 hours of gateway uptime
  • Average ~25 minutes between stalls
  • Each stall: 30s-2min of complete message loss (can't receive or send)
  • sendChatAction, editMessageText, sendMessage all fail during stall
  • Users perceive bot as "slow" or unresponsive

Log Evidence

[telegram] autoSelectFamily=true (default-node22)
[telegram] dnsResultOrder=ipv4first (default-node22)
[telegram] fetch fallback: enabling sticky IPv4-only dispatcher (codes=UND_ERR_CONNECT_TIMEOUT)
  ... works for a while ...
[telegram] Polling stall detected (no getUpdates for 99.27s); forcing restart.
[telegram] polling runner stopped (polling stall detected); restarting in 30s.
[telegram] autoSelectFamily=true (default-node22)          ← transport recreated, sticky lost
[telegram] dnsResultOrder=ipv4first (default-node22)
[telegram] fetch fallback: enabling sticky IPv4-only dispatcher (codes=UND_ERR_CONNECT_TIMEOUT)  ← has to re-discover

Proposed Fix

Hoist resolveTelegramTransport() out of createTelegramBot() so the transport (and its sticky fallback state) is resolved once and reused across polling restarts.

In src/telegram/bot.ts, accept a pre-resolved TelegramTransport in options, falling back to resolveTelegramTransport() only on first creation. The polling session would resolve the transport once and pass it to each bot instance.

Workaround

Users can set channels.telegram.network.autoSelectFamily: false in openclaw.json to permanently disable IPv6 for Telegram. This bypasses the sticky fallback entirely but requires the user to know about the config option.

Environment

  • OpenClaw 2026.3.13
  • Node 22+
  • macOS (Apple Silicon)
  • ISP: residential connection with unstable IPv6 routes to Telegram API

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions