fix: Telegram API requests fail with Network request failed after

Fixes #28835
This commit is contained in:
0xlin2023
2026-03-06 23:43:30 +08:00
committed by Ayaan Zaidi
parent d000316d19
commit e6bf69b366
3 changed files with 43 additions and 1 deletions

View File

@@ -40,7 +40,7 @@ describe("isRecoverableTelegramNetworkError", () => {
});
it("skips broad message matches for send context", () => {
const networkRequestErr = new Error("Network request for 'sendMessage' failed!");
const networkRequestErr = new Error("Network request for 'sendMessage' timed out!");
expect(isRecoverableTelegramNetworkError(networkRequestErr, { context: "send" })).toBe(false);
expect(isRecoverableTelegramNetworkError(networkRequestErr, { context: "polling" })).toBe(true);
@@ -49,6 +49,20 @@ describe("isRecoverableTelegramNetworkError", () => {
expect(isRecoverableTelegramNetworkError(undiciSnippetErr, { context: "polling" })).toBe(true);
});
it("treats grammY network envelope errors as recoverable in send context", () => {
expect(
isRecoverableTelegramNetworkError(new Error("Network request for 'sendMessage' failed!"), {
context: "send",
}),
).toBe(true);
expect(
isRecoverableTelegramNetworkError(
new Error("Network request for 'sendMessage' failed after 2 attempts."),
{ context: "send" },
),
).toBe(true);
});
it("returns false for unrelated errors", () => {
expect(isRecoverableTelegramNetworkError(new Error("invalid token"))).toBe(false);
});

View File

@@ -33,6 +33,8 @@ const RECOVERABLE_ERROR_NAMES = new Set([
]);
const ALWAYS_RECOVERABLE_MESSAGES = new Set(["fetch failed", "typeerror: fetch failed"]);
const GRAMMY_NETWORK_REQUEST_FAILED_RE =
/^network request(?:\s+for\s+["']?[^"']+["']?)?\s+failed(?:\s+after\b.*)?[!.]?$/i;
const RECOVERABLE_MESSAGE_SNIPPETS = [
"undici",
@@ -106,6 +108,9 @@ export function isRecoverableTelegramNetworkError(
if (message && ALWAYS_RECOVERABLE_MESSAGES.has(message)) {
return true;
}
if (message && GRAMMY_NETWORK_REQUEST_FAILED_RE.test(message)) {
return true;
}
if (allowMessageMatch && message) {
if (RECOVERABLE_MESSAGE_SNIPPETS.some((snippet) => message.includes(snippet))) {
return true;

View File

@@ -779,6 +779,29 @@ describe("sendMessageTelegram", () => {
expect(sendMessage).toHaveBeenCalledTimes(1);
});
it("retries when grammY network envelope message includes failed-after wording", async () => {
const chatId = "123";
const sendMessage = vi
.fn()
.mockRejectedValueOnce(new Error("Network request for 'sendMessage' failed after 1 attempts."))
.mockResolvedValueOnce({
message_id: 7,
chat: { id: chatId },
});
const api = { sendMessage } as unknown as {
sendMessage: typeof sendMessage;
};
const result = await sendMessageTelegram(chatId, "hi", {
token: "tok",
api,
retry: { attempts: 2, minDelayMs: 0, maxDelayMs: 0, jitter: 0 },
});
expect(sendMessage).toHaveBeenCalledTimes(2);
expect(result).toEqual({ messageId: "7", chatId });
});
it("sends GIF media as animation", async () => {
const chatId = "123";
const sendAnimation = vi.fn().mockResolvedValue({