mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 18:48:27 +00:00
fix(telegram): classify undici fetch errors as recoverable for retry (#16699)
Merged via /review-pr -> /prepare-pr -> /merge-pr.
Prepared head SHA: 67b5bce44f
Co-authored-by: Glucksberg <80581902+Glucksberg@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
This commit is contained in:
@@ -169,8 +169,12 @@ describe("monitorTelegramProvider (grammY)", () => {
|
||||
expect(api.sendMessage).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("retries on recoverable network errors", async () => {
|
||||
const networkError = Object.assign(new Error("timeout"), { code: "ETIMEDOUT" });
|
||||
it("retries on recoverable undici fetch errors", async () => {
|
||||
const networkError = Object.assign(new TypeError("fetch failed"), {
|
||||
cause: Object.assign(new Error("connect timeout"), {
|
||||
code: "UND_ERR_CONNECT_TIMEOUT",
|
||||
}),
|
||||
});
|
||||
runSpy
|
||||
.mockImplementationOnce(() => ({
|
||||
task: () => Promise.reject(networkError),
|
||||
|
||||
@@ -30,12 +30,25 @@ describe("isRecoverableTelegramNetworkError", () => {
|
||||
expect(isRecoverableTelegramNetworkError(new Error("Undici: socket failure"))).toBe(true);
|
||||
});
|
||||
|
||||
it("skips message matches for send context", () => {
|
||||
it("treats undici fetch failed errors as recoverable in send context", () => {
|
||||
const err = new TypeError("fetch failed");
|
||||
expect(isRecoverableTelegramNetworkError(err, { context: "send" })).toBe(false);
|
||||
expect(isRecoverableTelegramNetworkError(err, { context: "send" })).toBe(true);
|
||||
expect(
|
||||
isRecoverableTelegramNetworkError(new Error("TypeError: fetch failed"), { context: "send" }),
|
||||
).toBe(true);
|
||||
expect(isRecoverableTelegramNetworkError(err, { context: "polling" })).toBe(true);
|
||||
});
|
||||
|
||||
it("skips broad message matches for send context", () => {
|
||||
const networkRequestErr = new Error("Network request for 'sendMessage' failed!");
|
||||
expect(isRecoverableTelegramNetworkError(networkRequestErr, { context: "send" })).toBe(false);
|
||||
expect(isRecoverableTelegramNetworkError(networkRequestErr, { context: "polling" })).toBe(true);
|
||||
|
||||
const undiciSnippetErr = new Error("Undici: socket failure");
|
||||
expect(isRecoverableTelegramNetworkError(undiciSnippetErr, { context: "send" })).toBe(false);
|
||||
expect(isRecoverableTelegramNetworkError(undiciSnippetErr, { context: "polling" })).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false for unrelated errors", () => {
|
||||
expect(isRecoverableTelegramNetworkError(new Error("invalid token"))).toBe(false);
|
||||
});
|
||||
|
||||
@@ -27,9 +27,9 @@ const RECOVERABLE_ERROR_NAMES = new Set([
|
||||
"BodyTimeoutError",
|
||||
]);
|
||||
|
||||
const ALWAYS_RECOVERABLE_MESSAGES = new Set(["fetch failed", "typeerror: fetch failed"]);
|
||||
|
||||
const RECOVERABLE_MESSAGE_SNIPPETS = [
|
||||
"fetch failed",
|
||||
"typeerror: fetch failed",
|
||||
"undici",
|
||||
"network error",
|
||||
"network request",
|
||||
@@ -138,9 +138,12 @@ export function isRecoverableTelegramNetworkError(
|
||||
return true;
|
||||
}
|
||||
|
||||
if (allowMessageMatch) {
|
||||
const message = formatErrorMessage(candidate).toLowerCase();
|
||||
if (message && RECOVERABLE_MESSAGE_SNIPPETS.some((snippet) => message.includes(snippet))) {
|
||||
const message = formatErrorMessage(candidate).trim().toLowerCase();
|
||||
if (message && ALWAYS_RECOVERABLE_MESSAGES.has(message)) {
|
||||
return true;
|
||||
}
|
||||
if (allowMessageMatch && message) {
|
||||
if (RECOVERABLE_MESSAGE_SNIPPETS.some((snippet) => message.includes(snippet))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user