Skip to content

Commit ce15f34

Browse files
miorbnliclaude
andauthored
fix(telegram): use idempotent retry context for delete/reaction (#96612)
reactMessageTelegram and deleteMessageTelegram passed context: "send" to isRecoverableTelegramNetworkError, which disables message-snippet matching (allowMessageMatch defaults to false only for "send"). Both operations are idempotent (setMessageReaction / deleteMessage are safe to repeat), yet a transient snippet-only network error (e.g. "socket hang up", "undici network error" with no error code) was not retried — stricter than polling/webhook/ unknown, which all default allowMessageMatch to true. Users saw spurious reaction/delete failures on transient network errors. Add delete | react to TelegramNetworkErrorContext (additive) and use them at the two callers. The helper default (context !== "send") is unchanged, so delete/react now match polling/webhook/unknown. sendMessage keeps "send". Co-authored-by: Claude <[email protected]>
1 parent e5c3c59 commit ce15f34

3 files changed

Lines changed: 19 additions & 3 deletions

File tree

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,16 @@ describe("isRecoverableTelegramNetworkError", () => {
139139
expect(isRecoverableTelegramNetworkError(undiciSnippetErr, { context: "polling" })).toBe(true);
140140
});
141141

142+
it("treats delete/react (idempotent) contexts like polling, not send", () => {
143+
const undiciSnippetErr = new Error("Undici: socket failure");
144+
// delete and react are idempotent Telegram operations; a transient
145+
// snippet-only error must be retried (allowMessageMatch defaults true),
146+
// matching polling/webhook. send stays strict as the regression guard.
147+
expect(isRecoverableTelegramNetworkError(undiciSnippetErr, { context: "delete" })).toBe(true);
148+
expect(isRecoverableTelegramNetworkError(undiciSnippetErr, { context: "react" })).toBe(true);
149+
expect(isRecoverableTelegramNetworkError(undiciSnippetErr, { context: "send" })).toBe(false);
150+
});
151+
142152
it("treats grammY failed-after envelope errors as recoverable in send context", () => {
143153
expect(
144154
isRecoverableTelegramNetworkError(

extensions/telegram/src/network-errors.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,13 @@ export function isTelegramMisdirectedRequestError(err: unknown): boolean {
141141
return false;
142142
}
143143

144-
export type TelegramNetworkErrorContext = "polling" | "send" | "webhook" | "unknown";
144+
export type TelegramNetworkErrorContext =
145+
| "polling"
146+
| "send"
147+
| "webhook"
148+
| "delete"
149+
| "react"
150+
| "unknown";
145151
export type TelegramNetworkErrorOrigin = {
146152
method?: string | null;
147153
url?: string | null;

extensions/telegram/src/send.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,7 +1219,7 @@ export async function reactMessageTelegram(
12191219
account,
12201220
retry: opts.retry,
12211221
verbose: opts.verbose,
1222-
shouldRetry: (err) => isRecoverableTelegramNetworkError(err, { context: "send" }),
1222+
shouldRetry: (err) => isRecoverableTelegramNetworkError(err, { context: "react" }),
12231223
});
12241224
const remove = opts.remove === true;
12251225
const trimmedEmoji = emoji.trim();
@@ -1276,7 +1276,7 @@ export async function deleteMessageTelegram(
12761276
account,
12771277
retry: opts.retry,
12781278
verbose: opts.verbose,
1279-
shouldRetry: (err) => isRecoverableTelegramNetworkError(err, { context: "send" }),
1279+
shouldRetry: (err) => isRecoverableTelegramNetworkError(err, { context: "delete" }),
12801280
});
12811281
try {
12821282
await requestWithDiag(() => api.deleteMessage(chatId, messageId), "deleteMessage", {

0 commit comments

Comments
 (0)