From 0c5471ef8e9a422ac3420eb7e89c8fb60887b3ab Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Mon, 13 Apr 2026 17:26:48 +0100 Subject: [PATCH] fix(telegram): retry failed commands pagination callbacks --- .../telegram/src/bot-handlers.runtime.ts | 2 +- .../src/bot.create-telegram-bot.test.ts | 52 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/extensions/telegram/src/bot-handlers.runtime.ts b/extensions/telegram/src/bot-handlers.runtime.ts index 7164fec224a..731bb775a02 100644 --- a/extensions/telegram/src/bot-handlers.runtime.ts +++ b/extensions/telegram/src/bot-handlers.runtime.ts @@ -1434,7 +1434,7 @@ export const registerTelegramHandlers = ({ } catch (editErr) { const errStr = String(editErr); if (!errStr.includes("message is not modified")) { - throw editErr; + throw new TelegramRetryableCallbackError(editErr); } } return; diff --git a/extensions/telegram/src/bot.create-telegram-bot.test.ts b/extensions/telegram/src/bot.create-telegram-bot.test.ts index 6698294c049..81e40e930be 100644 --- a/extensions/telegram/src/bot.create-telegram-bot.test.ts +++ b/extensions/telegram/src/bot.create-telegram-bot.test.ts @@ -2932,4 +2932,56 @@ describe("createTelegramBot", () => { expect(editMessageTextSpy).toHaveBeenCalledTimes(1); expect(editMessageTextSpy.mock.calls[0]?.[2]).toContain("Select a provider:"); }); + + it("retries command pagination callbacks after a bubbled edit failure", async () => { + createTelegramBot({ token: "tok" }); + const callbackHandler = getOnHandler("callback_query"); + const middlewares = middlewareUseSpy.mock.calls + .map((call) => call[0]) + .filter( + (fn): fn is (ctx: Record, next: () => Promise) => Promise => + typeof fn === "function", + ); + const runMiddlewareChain = async (ctx: Record) => { + let idx = -1; + const dispatch = async (i: number): Promise => { + if (i <= idx) { + throw new Error("middleware dispatch called multiple times"); + } + idx = i; + const fn = middlewares[i]; + if (!fn) { + await callbackHandler(ctx); + return; + } + await fn(ctx, async () => dispatch(i + 1)); + }; + await dispatch(0); + }; + + const ctx = { + update: { update_id: 777 }, + callbackQuery: { + id: "cbq-commands-retry-1", + data: "commands_page_2:main", + from: { id: 9, first_name: "Ada", username: "ada_bot" }, + message: { + chat: { id: 1234, type: "private" }, + date: 1736380800, + message_id: 19, + }, + }, + me: { username: "openclaw_bot" }, + getFile: async () => ({ download: async () => new Uint8Array() }), + }; + + editMessageTextSpy.mockImplementationOnce(async () => { + throw new Error("edit boom"); + }); + await expect(runMiddlewareChain(ctx)).rejects.toThrow("edit boom"); + await runMiddlewareChain(ctx); + + expect(editMessageTextSpy).toHaveBeenCalledTimes(2); + expect(editMessageTextSpy.mock.calls.at(-1)?.[2]).toContain("Commands (2/"); + }); });