fix(telegram): fallback to alternative API IP when DNS-resolved endpoint is unreachable#48812
fix(telegram): fallback to alternative API IP when DNS-resolved endpoint is unreachable#48812Cypherm wants to merge 1 commit intoopenclaw:mainfrom
Conversation
Greptile SummaryThis PR adds a third tier to the existing Telegram transport retry chain in
Confidence Score: 4/5
Prompt To Fix All With AIThis is a comment left during a code review.
Path: extensions/telegram/src/fetch.ts
Line: 49
Comment:
**Hardcoded fallback IP with no update mechanism**
`149.154.167.220` is hardcoded as the only fallback IP with no rotation, staleness detection, or easy update path. If Telegram retires or reassigns this IP (even if stable today), the fallback tier will silently fail for all users with the bad DNS routing — which is precisely the scenario this PR is designed to fix.
Consider at minimum adding a comment documenting how to verify and update this IP (e.g. `dig api.telegram.org` or `curl -sv https://api.telegram.org`), or expanding the array to include the full set of known Telegram Bot API IPs (Telegram publicly documents ranges `149.154.167.0/22` and `91.108.4.0/22`). Having more than one IP increases resilience and makes future rotation less urgent.
```suggestion
const TELEGRAM_FALLBACK_IPS: readonly string[] = [
"149.154.167.220", // primary fallback – verify periodically with `dig api.telegram.org`
];
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: extensions/telegram/src/fetch.ts
Line: 144-149
Comment:
**`family` is hardcoded to `4` for all fallback addresses**
All entries in `fallbackIps` are reported as `family: 4` regardless of their actual format. This is correct for the current single IPv4 entry, but if an IPv6 address is ever added to `TELEGRAM_FALLBACK_IPS` the lookup would advertise an IPv6 address as family 4, which undici would likely reject or misroute.
Consider detecting the family dynamically using `net.isIPv4` / `net.isIPv6`:
```suggestion
if (opts && "all" in opts && opts.all) {
(callback as (err: null, addresses: dns.LookupAddress[]) => void)(
null,
fallbackIps.map((addr) => ({
address: addr,
family: net.isIPv4(addr) ? 4 : 6,
})),
);
} else {
(callback as (err: null, address: string, family: number) => void)(
null,
ip,
net.isIPv4(ip) ? 4 : 6,
);
}
```
(Requires `import * as net from "node:net"` at the top of the file.)
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: extensions/telegram/src/fetch.ts
Line: 527-529
Comment:
**`connect` and `proxyTls` share the same object reference**
The established pattern throughout this file (see `resolveTelegramDispatcherPolicy` and `createTelegramDispatcher`) always passes independent copies of the connect options to `connect` and `proxyTls`:
```ts
connect: { ...connect }, proxyTls: { ...connect }
```
Here, both fields receive the same object reference. If `EnvHttpProxyAgent` ever mutates the options object during construction or request dispatch (e.g., to normalise or cache a field), mutations through one reference would silently affect the other. Align with the existing pattern:
```suggestion
fallbackIpDispatcher = stickyShouldUseEnvProxy
? new EnvHttpProxyAgent({ connect: { ...connect }, proxyTls: { ...connect } })
: new Agent({ connect });
```
How can I resolve this? If you propose a fix, please make it concise.Last reviewed commit: 29a3d59 |
| * Used as a last-resort fallback when the DNS-resolved IP is unreachable. | ||
| * Connected via DNS pinning (custom lookup) so TLS validates against api.telegram.org. | ||
| */ | ||
| const TELEGRAM_FALLBACK_IPS: readonly string[] = ["149.154.167.220"]; |
There was a problem hiding this comment.
Hardcoded fallback IP with no update mechanism
149.154.167.220 is hardcoded as the only fallback IP with no rotation, staleness detection, or easy update path. If Telegram retires or reassigns this IP (even if stable today), the fallback tier will silently fail for all users with the bad DNS routing — which is precisely the scenario this PR is designed to fix.
Consider at minimum adding a comment documenting how to verify and update this IP (e.g. dig api.telegram.org or curl -sv https://api.telegram.org), or expanding the array to include the full set of known Telegram Bot API IPs (Telegram publicly documents ranges 149.154.167.0/22 and 91.108.4.0/22). Having more than one IP increases resilience and makes future rotation less urgent.
| const TELEGRAM_FALLBACK_IPS: readonly string[] = ["149.154.167.220"]; | |
| const TELEGRAM_FALLBACK_IPS: readonly string[] = [ | |
| "149.154.167.220", // primary fallback – verify periodically with `dig api.telegram.org` | |
| ]; |
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/telegram/src/fetch.ts
Line: 49
Comment:
**Hardcoded fallback IP with no update mechanism**
`149.154.167.220` is hardcoded as the only fallback IP with no rotation, staleness detection, or easy update path. If Telegram retires or reassigns this IP (even if stable today), the fallback tier will silently fail for all users with the bad DNS routing — which is precisely the scenario this PR is designed to fix.
Consider at minimum adding a comment documenting how to verify and update this IP (e.g. `dig api.telegram.org` or `curl -sv https://api.telegram.org`), or expanding the array to include the full set of known Telegram Bot API IPs (Telegram publicly documents ranges `149.154.167.0/22` and `91.108.4.0/22`). Having more than one IP increases resilience and makes future rotation less urgent.
```suggestion
const TELEGRAM_FALLBACK_IPS: readonly string[] = [
"149.154.167.220", // primary fallback – verify periodically with `dig api.telegram.org`
];
```
How can I resolve this? If you propose a fix, please make it concise.| (callback as (err: null, addresses: dns.LookupAddress[]) => void)( | ||
| null, | ||
| fallbackIps.map((addr) => ({ address: addr, family: 4 })), | ||
| ); | ||
| } else { | ||
| (callback as (err: null, address: string, family: number) => void)(null, ip, 4); |
There was a problem hiding this comment.
family is hardcoded to 4 for all fallback addresses
All entries in fallbackIps are reported as family: 4 regardless of their actual format. This is correct for the current single IPv4 entry, but if an IPv6 address is ever added to TELEGRAM_FALLBACK_IPS the lookup would advertise an IPv6 address as family 4, which undici would likely reject or misroute.
Consider detecting the family dynamically using net.isIPv4 / net.isIPv6:
| (callback as (err: null, addresses: dns.LookupAddress[]) => void)( | |
| null, | |
| fallbackIps.map((addr) => ({ address: addr, family: 4 })), | |
| ); | |
| } else { | |
| (callback as (err: null, address: string, family: number) => void)(null, ip, 4); | |
| if (opts && "all" in opts && opts.all) { | |
| (callback as (err: null, addresses: dns.LookupAddress[]) => void)( | |
| null, | |
| fallbackIps.map((addr) => ({ | |
| address: addr, | |
| family: net.isIPv4(addr) ? 4 : 6, | |
| })), | |
| ); | |
| } else { | |
| (callback as (err: null, address: string, family: number) => void)( | |
| null, | |
| ip, | |
| net.isIPv4(ip) ? 4 : 6, | |
| ); | |
| } |
(Requires import * as net from "node:net" at the top of the file.)
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/telegram/src/fetch.ts
Line: 144-149
Comment:
**`family` is hardcoded to `4` for all fallback addresses**
All entries in `fallbackIps` are reported as `family: 4` regardless of their actual format. This is correct for the current single IPv4 entry, but if an IPv6 address is ever added to `TELEGRAM_FALLBACK_IPS` the lookup would advertise an IPv6 address as family 4, which undici would likely reject or misroute.
Consider detecting the family dynamically using `net.isIPv4` / `net.isIPv6`:
```suggestion
if (opts && "all" in opts && opts.all) {
(callback as (err: null, addresses: dns.LookupAddress[]) => void)(
null,
fallbackIps.map((addr) => ({
address: addr,
family: net.isIPv4(addr) ? 4 : 6,
})),
);
} else {
(callback as (err: null, address: string, family: number) => void)(
null,
ip,
net.isIPv4(ip) ? 4 : 6,
);
}
```
(Requires `import * as net from "node:net"` at the top of the file.)
How can I resolve this? If you propose a fix, please make it concise.| fallbackIpDispatcher = stickyShouldUseEnvProxy | ||
| ? new EnvHttpProxyAgent({ connect, proxyTls: connect }) | ||
| : new Agent({ connect }); |
There was a problem hiding this comment.
connect and proxyTls share the same object reference
The established pattern throughout this file (see resolveTelegramDispatcherPolicy and createTelegramDispatcher) always passes independent copies of the connect options to connect and proxyTls:
connect: { ...connect }, proxyTls: { ...connect }Here, both fields receive the same object reference. If EnvHttpProxyAgent ever mutates the options object during construction or request dispatch (e.g., to normalise or cache a field), mutations through one reference would silently affect the other. Align with the existing pattern:
| fallbackIpDispatcher = stickyShouldUseEnvProxy | |
| ? new EnvHttpProxyAgent({ connect, proxyTls: connect }) | |
| : new Agent({ connect }); | |
| fallbackIpDispatcher = stickyShouldUseEnvProxy | |
| ? new EnvHttpProxyAgent({ connect: { ...connect }, proxyTls: { ...connect } }) | |
| : new Agent({ connect }); |
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/telegram/src/fetch.ts
Line: 527-529
Comment:
**`connect` and `proxyTls` share the same object reference**
The established pattern throughout this file (see `resolveTelegramDispatcherPolicy` and `createTelegramDispatcher`) always passes independent copies of the connect options to `connect` and `proxyTls`:
```ts
connect: { ...connect }, proxyTls: { ...connect }
```
Here, both fields receive the same object reference. If `EnvHttpProxyAgent` ever mutates the options object during construction or request dispatch (e.g., to normalise or cache a field), mutations through one reference would silently affect the other. Align with the existing pattern:
```suggestion
fallbackIpDispatcher = stickyShouldUseEnvProxy
? new EnvHttpProxyAgent({ connect: { ...connect }, proxyTls: { ...connect } })
: new Agent({ connect });
```
How can I resolve this? If you propose a fix, please make it concise.29a3d59 to
c040a6f
Compare
c040a6f to
065548d
Compare
|
Superseded by #49148. I kept the same fallback goal, but rewrote it as one ordered transport retry chain shared by normal Telegram API calls and media downloads, instead of adding another special-case branch inside |
|
Closing as duplicate; this was superseded by #49148. |
Summary
When
api.telegram.orgDNS resolves to an IP that is unreachable from certain ISPs (confirmed: Taiwan/HiNet), the bot enters a permanent failure loop — default fetch times out, IPv4-only retry also times out (same bad IP), polling restarts, repeat forever.This PR adds a third fallback tier to the existing retry chain in
resolveTelegramTransport():connect.lookup) → successThe fallback uses the same technique as
curl --resolve: connects to an alternative IP (149.154.167.220) while TLS validates againstapi.telegram.orgvia SNI. This is fully transparent — no config changes, no user intervention.Key properties
NO_PROXYbypass and env proxy settingsVerification
Tested by simulating DNS failure via
/etc/hostspointingapi.telegram.orgto an unreachable IP (192.0.2.1):Also verified that all three DNS resolvers (system, Google 8.8.8.8, Cloudflare 1.1.1.1) return the same IP for
api.telegram.org, confirming that DNS-level fallback would not help — direct IP pinning is the correct approach.Relates to #45372, #28835
Test plan
fetch.test.tstests pass (18 pre-existing failures unrelated to this change, 2 passing — confirmed identical with and without patch)pnpm format:check)pnpm tsgo— no new errors)/etc/hostsoverride to unreachable IP → fallback recovers automatically🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 4.6 [email protected]