mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:50:43 +00:00
fix: quiet telegram ipv4 fallback noise
This commit is contained in:
@@ -23,6 +23,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- Telegram: inherit the process DNS result order for Bot API transport and downgrade recovered sticky IPv4 fallback promotions to debug logs, while keeping pinned-IP escalation warnings visible. Fixes #75904. Thanks @highfly-hi and @neeravmakwana.
|
||||
- Subagents: honor `sessions_spawn` with `expectsCompletionMessage: false` by skipping parent completion handoff delivery while still running child cleanup. Fixes #75848. Thanks @alfredjbclaw.
|
||||
- Gateway/logging: keep deferred channel startup logs on the subsystem logger, so Slack, Discord, Telegram, and voice-call startup messages keep timestamped prefixes. Thanks @vincentkoc.
|
||||
- ACP/Discord: suppress completion announce delivery for inline thread-bound ACP session runs, so Discord thread-bound ACP replies are not delivered twice. Fixes #60780. Thanks @solavrc.
|
||||
|
||||
@@ -878,7 +878,7 @@ channels:
|
||||
proxy: socks5://<user>:<password>@proxy-host:1080
|
||||
```
|
||||
|
||||
- Node 22+ defaults to `autoSelectFamily=true` (except WSL2) and `dnsResultOrder=ipv4first`.
|
||||
- Node 22+ defaults to `autoSelectFamily=true` (except WSL2). Telegram DNS result order honors `OPENCLAW_TELEGRAM_DNS_RESULT_ORDER`, then `channels.telegram.network.dnsResultOrder`, then the process default such as `NODE_OPTIONS=--dns-result-order=ipv4first`; if none applies, Node 22+ falls back to `ipv4first`.
|
||||
- If your host is WSL2 or explicitly works better with IPv4-only behavior, force family selection:
|
||||
|
||||
```yaml
|
||||
|
||||
@@ -2,9 +2,11 @@ import { resolveFetch } from "openclaw/plugin-sdk/fetch-runtime";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const setDefaultResultOrder = vi.hoisted(() => vi.fn());
|
||||
const getDefaultResultOrder = vi.hoisted(() => vi.fn(() => "ipv4first"));
|
||||
const setDefaultAutoSelectFamily = vi.hoisted(() => vi.fn());
|
||||
const loggerInfo = vi.hoisted(() => vi.fn());
|
||||
const loggerDebug = vi.hoisted(() => vi.fn());
|
||||
const loggerWarn = vi.hoisted(() => vi.fn());
|
||||
|
||||
const undiciFetch = vi.hoisted(() => vi.fn());
|
||||
const setGlobalDispatcher = vi.hoisted(() => vi.fn());
|
||||
@@ -46,6 +48,7 @@ vi.mock("node:dns", async () => {
|
||||
const actual = await vi.importActual<typeof import("node:dns")>("node:dns");
|
||||
return {
|
||||
...actual,
|
||||
getDefaultResultOrder,
|
||||
setDefaultResultOrder,
|
||||
};
|
||||
});
|
||||
@@ -70,12 +73,12 @@ vi.mock("openclaw/plugin-sdk/runtime-env", () => ({
|
||||
createSubsystemLogger: () => ({
|
||||
info: loggerInfo,
|
||||
debug: loggerDebug,
|
||||
warn: vi.fn(),
|
||||
warn: loggerWarn,
|
||||
error: vi.fn(),
|
||||
child: () => ({
|
||||
info: loggerInfo,
|
||||
debug: loggerDebug,
|
||||
warn: vi.fn(),
|
||||
warn: loggerWarn,
|
||||
error: vi.fn(),
|
||||
}),
|
||||
}),
|
||||
@@ -129,6 +132,9 @@ beforeEach(() => {
|
||||
}
|
||||
loggerInfo.mockReset();
|
||||
loggerDebug.mockReset();
|
||||
loggerWarn.mockReset();
|
||||
getDefaultResultOrder.mockReset();
|
||||
getDefaultResultOrder.mockReturnValue("ipv4first");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -368,9 +374,9 @@ describe("resolveTelegramFetch", () => {
|
||||
resolveTelegramFetchOrThrow();
|
||||
|
||||
expect(loggerInfo).not.toHaveBeenCalledWith("autoSelectFamily=true (default-node22)");
|
||||
expect(loggerInfo).not.toHaveBeenCalledWith("dnsResultOrder=ipv4first (default-node22)");
|
||||
expect(loggerInfo).not.toHaveBeenCalledWith("dnsResultOrder=ipv4first (process-default)");
|
||||
expect(loggerDebug).toHaveBeenCalledWith("autoSelectFamily=true (default-node22)");
|
||||
expect(loggerDebug).toHaveBeenCalledWith("dnsResultOrder=ipv4first (default-node22)");
|
||||
expect(loggerDebug).toHaveBeenCalledWith("dnsResultOrder=ipv4first (process-default)");
|
||||
});
|
||||
|
||||
it("uses EnvHttpProxyAgent dispatcher when proxy env is configured", async () => {
|
||||
@@ -813,6 +819,12 @@ describe("resolveTelegramFetch", () => {
|
||||
autoSelectFamily: false,
|
||||
}),
|
||||
);
|
||||
expect(loggerDebug).toHaveBeenCalledWith(
|
||||
expect.stringContaining("fetch fallback: enabling sticky IPv4-only dispatcher"),
|
||||
);
|
||||
expect(loggerWarn).not.toHaveBeenCalledWith(
|
||||
expect.stringContaining("fetch fallback: enabling sticky IPv4-only dispatcher"),
|
||||
);
|
||||
});
|
||||
|
||||
it("escalates from IPv4 fallback to pinned Telegram IP and keeps it sticky", async () => {
|
||||
@@ -841,6 +853,9 @@ describe("resolveTelegramFetch", () => {
|
||||
expect(secondDispatcher).not.toBe(thirdDispatcher);
|
||||
expect(thirdDispatcher).toBe(fourthDispatcher);
|
||||
expectPinnedFallbackIpDispatcher(3);
|
||||
expect(loggerWarn).toHaveBeenCalledWith(
|
||||
expect.stringContaining("fetch fallback: DNS-resolved IP unreachable"),
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps the armed fallback sticky when all attempts fail", async () => {
|
||||
|
||||
@@ -75,6 +75,7 @@ type TelegramDispatcherAttempt = {
|
||||
type TelegramTransportAttempt = {
|
||||
createDispatcher: () => TelegramDispatcher;
|
||||
exportAttempt: TelegramDispatcherAttempt;
|
||||
logLevel?: "debug" | "warn";
|
||||
logMessage?: string;
|
||||
};
|
||||
|
||||
@@ -518,6 +519,7 @@ function createTelegramTransportAttempts(params: {
|
||||
return ipv4Dispatcher;
|
||||
},
|
||||
exportAttempt: { dispatcherPolicy: fallbackPolicy },
|
||||
logLevel: "debug",
|
||||
logMessage: "fetch fallback: enabling sticky IPv4-only dispatcher",
|
||||
});
|
||||
|
||||
@@ -542,6 +544,7 @@ function createTelegramTransportAttempts(params: {
|
||||
return fallbackIpDispatcher;
|
||||
},
|
||||
exportAttempt: { dispatcherPolicy: fallbackIpPolicy },
|
||||
logLevel: "warn",
|
||||
logMessage: "fetch fallback: DNS-resolved IP unreachable; trying alternative Telegram API IP",
|
||||
});
|
||||
|
||||
@@ -643,7 +646,12 @@ export function resolveTelegramTransport(
|
||||
const nextAttempt = transportAttempts[nextIndex];
|
||||
if (nextAttempt.logMessage) {
|
||||
const reasonText = reason ? `, reason=${reason}` : "";
|
||||
log.warn(`${nextAttempt.logMessage} (codes=${formatErrorCodes(err)}${reasonText})`);
|
||||
const logLine = `${nextAttempt.logMessage} (codes=${formatErrorCodes(err)}${reasonText})`;
|
||||
if (nextAttempt.logLevel === "debug") {
|
||||
log.debug(logLine);
|
||||
} else {
|
||||
log.warn(logLine);
|
||||
}
|
||||
}
|
||||
stickyAttemptIndex = nextIndex;
|
||||
return true;
|
||||
|
||||
@@ -202,31 +202,53 @@ describe("resolveTelegramDnsResultOrderDecision", () => {
|
||||
name: "ignores invalid env and config values before applying Node 22 default",
|
||||
env: { OPENCLAW_TELEGRAM_DNS_RESULT_ORDER: "bogus" },
|
||||
network: { dnsResultOrder: "invalid" } as unknown as TelegramNetworkConfig,
|
||||
defaultResultOrder: "ipv6first",
|
||||
nodeMajor: 22,
|
||||
expected: { value: "ipv4first", source: "default-node22" },
|
||||
},
|
||||
{
|
||||
name: "inherits process default when env and config are unset",
|
||||
defaultResultOrder: "ipv4first",
|
||||
nodeMajor: 20,
|
||||
expected: { value: "ipv4first", source: "process-default" },
|
||||
},
|
||||
{
|
||||
name: "prefers config over process default",
|
||||
network: { dnsResultOrder: "verbatim" },
|
||||
defaultResultOrder: "ipv4first",
|
||||
nodeMajor: 20,
|
||||
expected: { value: "verbatim", source: "config" },
|
||||
},
|
||||
] satisfies Array<{
|
||||
name: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
network?: TelegramNetworkConfig;
|
||||
defaultResultOrder?: string | null;
|
||||
nodeMajor: number;
|
||||
expected: ReturnType<typeof resolveTelegramDnsResultOrderDecision>;
|
||||
}>)("$name", ({ env, network, nodeMajor, expected }) => {
|
||||
}>)("$name", ({ env, network, defaultResultOrder, nodeMajor, expected }) => {
|
||||
const decision = resolveTelegramDnsResultOrderDecision({
|
||||
env,
|
||||
network,
|
||||
defaultResultOrder,
|
||||
nodeMajor,
|
||||
});
|
||||
expect(decision).toEqual(expected);
|
||||
});
|
||||
|
||||
it("defaults to ipv4first on Node 22", () => {
|
||||
const decision = resolveTelegramDnsResultOrderDecision({ nodeMajor: 22 });
|
||||
const decision = resolveTelegramDnsResultOrderDecision({
|
||||
defaultResultOrder: null,
|
||||
nodeMajor: 22,
|
||||
});
|
||||
expect(decision).toEqual({ value: "ipv4first", source: "default-node22" });
|
||||
});
|
||||
|
||||
it("returns null when no dns decision applies", () => {
|
||||
const decision = resolveTelegramDnsResultOrderDecision({ nodeMajor: 20 });
|
||||
const decision = resolveTelegramDnsResultOrderDecision({
|
||||
defaultResultOrder: null,
|
||||
nodeMajor: 20,
|
||||
});
|
||||
expect(decision).toEqual({ value: null });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import * as dns from "node:dns";
|
||||
import process from "node:process";
|
||||
import type { TelegramNetworkConfig } from "openclaw/plugin-sdk/config-types";
|
||||
import { isTruthyEnvValue, isWSL2Sync } from "openclaw/plugin-sdk/runtime-env";
|
||||
@@ -66,12 +67,14 @@ export function resolveTelegramAutoSelectFamilyDecision(params?: {
|
||||
* Priority:
|
||||
* 1. Environment variable OPENCLAW_TELEGRAM_DNS_RESULT_ORDER
|
||||
* 2. Config: channels.telegram.network.dnsResultOrder
|
||||
* 3. Default: "ipv4first" on Node 22+ (to work around common IPv6 issues)
|
||||
* 3. Process default: dns.getDefaultResultOrder()
|
||||
* 4. Default: "ipv4first" on Node 22+ (to work around common IPv6 issues)
|
||||
*/
|
||||
export function resolveTelegramDnsResultOrderDecision(params?: {
|
||||
network?: TelegramNetworkConfig;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
nodeMajor?: number;
|
||||
defaultResultOrder?: string | null;
|
||||
}): TelegramDnsResultOrderDecision {
|
||||
const env = params?.env ?? process.env;
|
||||
const nodeMajor =
|
||||
@@ -93,6 +96,15 @@ export function resolveTelegramDnsResultOrderDecision(params?: {
|
||||
return { value: configValue, source: "config" };
|
||||
}
|
||||
|
||||
const processDefaultValue = normalizeOptionalLowercaseString(
|
||||
params && "defaultResultOrder" in params
|
||||
? params.defaultResultOrder
|
||||
: dns.getDefaultResultOrder?.(),
|
||||
);
|
||||
if (processDefaultValue === "ipv4first" || processDefaultValue === "verbatim") {
|
||||
return { value: processDefaultValue, source: "process-default" };
|
||||
}
|
||||
|
||||
// Default to ipv4first on Node 22+ to avoid IPv6 issues
|
||||
if (Number.isFinite(nodeMajor) && nodeMajor >= 22) {
|
||||
return { value: "ipv4first", source: "default-node22" };
|
||||
|
||||
Reference in New Issue
Block a user