Skip to content

Commit 22dfb15

Browse files
lzw112vincentkoc
andauthored
fix(telegram): add missing 'action' retry context for sendChatAction (#100762)
* fix(telegram): add missing 'action' retry context for sendChatAction * fix(telegram): cover all sendChatAction retry paths --------- Co-authored-by: Vincent Koc <[email protected]>
1 parent a13a198 commit 22dfb15

6 files changed

Lines changed: 30 additions & 5 deletions

File tree

extensions/telegram/src/network-errors.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,14 +140,15 @@ describe("isRecoverableTelegramNetworkError", () => {
140140
expect(isRecoverableTelegramNetworkError(undiciSnippetErr, { context: "polling" })).toBe(true);
141141
});
142142

143-
it("treats delete/react/edit (idempotent) contexts like polling, not send", () => {
143+
it("treats delete/react/edit/action (idempotent) contexts like polling, not send", () => {
144144
const undiciSnippetErr = new Error("Undici: socket failure");
145-
// delete, react, and edit are idempotent Telegram operations; a transient
146-
// snippet-only error must be retried (allowMessageMatch defaults true),
145+
// delete, react, edit, and action are idempotent or non-message operations;
146+
// a transient snippet-only error must be retried (allowMessageMatch defaults true),
147147
// matching polling/webhook. send stays strict as the regression guard.
148148
expect(isRecoverableTelegramNetworkError(undiciSnippetErr, { context: "delete" })).toBe(true);
149149
expect(isRecoverableTelegramNetworkError(undiciSnippetErr, { context: "react" })).toBe(true);
150150
expect(isRecoverableTelegramNetworkError(undiciSnippetErr, { context: "edit" })).toBe(true);
151+
expect(isRecoverableTelegramNetworkError(undiciSnippetErr, { context: "action" })).toBe(true);
151152
expect(isRecoverableTelegramNetworkError(undiciSnippetErr, { context: "send" })).toBe(false);
152153
});
153154

extensions/telegram/src/network-errors.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ export type TelegramNetworkErrorContext =
148148
| "delete"
149149
| "react"
150150
| "edit"
151+
| "action"
151152
| "unknown";
152153
export type TelegramNetworkErrorOrigin = {
153154
method?: string | null;

extensions/telegram/src/send.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,28 @@ describe("sendMessageTelegram", () => {
638638
});
639639
});
640640

641+
it("retries snippet-only network errors when sending typing", async () => {
642+
loadConfig.mockReturnValue({
643+
channels: {
644+
telegram: {
645+
botToken: "tok",
646+
},
647+
},
648+
});
649+
botApi.sendChatAction
650+
.mockRejectedValueOnce(new Error("socket hang up"))
651+
.mockResolvedValue(true);
652+
653+
await sendTypingTelegram("telegram:group:-1001234567890", {
654+
cfg: TELEGRAM_TEST_CFG,
655+
token: "tok",
656+
accountId: "default",
657+
retry: { attempts: 2, minDelayMs: 0, maxDelayMs: 0, jitter: 0 },
658+
});
659+
660+
expect(botApi.sendChatAction).toHaveBeenCalledTimes(2);
661+
});
662+
641663
it("pins and unpins Telegram messages", async () => {
642664
loadConfig.mockReturnValue({
643665
channels: {

extensions/telegram/src/send.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1438,7 +1438,7 @@ export async function sendTypingTelegram(
14381438
account,
14391439
retry: opts.retry,
14401440
verbose: opts.verbose,
1441-
shouldRetry: (err) => isRecoverableTelegramNetworkError(err, { context: "send" }),
1441+
shouldRetry: (err) => isRecoverableTelegramNetworkError(err, { context: "action" }),
14421442
});
14431443
const threadParams = buildTypingThreadParams(target.messageThreadId ?? opts.messageThreadId);
14441444
await requestWithDiag(

extensions/telegram/src/sendchataction-401-backoff.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ describe("createTelegramSendChatActionHandler", () => {
178178

179179
it.each([
180180
["recoverable network", () => makeNetworkError(), 1000],
181+
["snippet-only network", () => new Error("socket hang up"), 1000],
181182
["Telegram 429", () => makeTelegramError("Too Many Requests", 429, { retry_after: 2 }), 2000],
182183
["Telegram 5xx", () => makeTelegramError("Bad Gateway", 502), 1000],
183184
])("cools down transient %s errors", async (_name, makeError, expectedCooldownMs) => {

extensions/telegram/src/sendchataction-401-backoff.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ function isTransientSendChatActionError(error: unknown): boolean {
100100
return (
101101
isTelegramRateLimitError(error) ||
102102
isTelegramServerError(error) ||
103-
isRecoverableTelegramNetworkError(error, { context: "send" })
103+
isRecoverableTelegramNetworkError(error, { context: "action" })
104104
);
105105
}
106106

0 commit comments

Comments
 (0)