From 00218661a88b2091ef3bbe7d3d45d6c5e56c0b62 Mon Sep 17 00:00:00 2001 From: Ayaan Zaidi Date: Tue, 3 Feb 2026 10:06:06 +0530 Subject: [PATCH] fix: tolerate markdown stream parse errors --- src/telegram/edit-stream.ts | 24 +++++++++++++++++++----- src/telegram/format.ts | 13 +++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/telegram/edit-stream.ts b/src/telegram/edit-stream.ts index 8666e1195884..2f2eaa3b18b7 100644 --- a/src/telegram/edit-stream.ts +++ b/src/telegram/edit-stream.ts @@ -9,16 +9,20 @@ import { formatErrorMessage } from "../infra/errors.js"; import { createTelegramRetryRunner } from "../infra/retry-policy.js"; import { withTelegramApiErrorLogging } from "./api-logging.js"; import { buildTelegramThreadParams, type TelegramThreadSpec } from "./bot/helpers.js"; -import { renderTelegramHtmlText } from "./format.js"; +import { markdownToTelegramPlainText, renderTelegramHtmlText } from "./format.js"; import { isRecoverableTelegramNetworkError } from "./network-errors.js"; import { editMessageTelegram } from "./send.js"; const PARSE_ERR_RE = /can't parse entities|parse entities|find end of the entity/i; +const BAD_REQUEST_RE = /bad request/i; const NOT_MODIFIED_RE = /message is not modified/i; const TELEGRAM_TEXT_MAX = 4096; const normalizeText = (text: string) => text.trimEnd(); +const shouldFallbackPlain = (errText: string) => + PARSE_ERR_RE.test(errText) || BAD_REQUEST_RE.test(errText); + const getCutoff = (isGroup: boolean, contentLength: number) => { if (isGroup) { if (contentLength > 1000) return 180; @@ -91,6 +95,7 @@ export function createTelegramEditStream(params: { }); const linkPreviewEnabled = params.linkPreview ?? true; const linkPreviewOptions = linkPreviewEnabled ? undefined : { is_disabled: true }; + const renderPlainText = (value: string) => markdownToTelegramPlainText(value, { tableMode }); const request = createTelegramRetryRunner({ configRetry: params.retry, verbose: false, @@ -133,12 +138,13 @@ export function createTelegramEditStream(params: { "message", ).catch(async (err) => { const errText = formatErrorMessage(err); - if (!PARSE_ERR_RE.test(errText)) { + if (!shouldFallbackPlain(errText)) { throw err; } + const fallbackText = renderPlainText(text); return await requestWithDiag( () => - params.api.sendMessage(chatId, text, { + params.api.sendMessage(chatId, fallbackText, { ...paramsWithPreview, }), "message-plain", @@ -151,8 +157,9 @@ export function createTelegramEditStream(params: { if (!messageId) { return; } + const currentMessageId = messageId; try { - await editMessageTelegram(chatId, messageId, text, { + await editMessageTelegram(chatId, currentMessageId, text, { api: params.api, cfg: params.cfg, accountId: params.accountId ?? undefined, @@ -160,7 +167,14 @@ export function createTelegramEditStream(params: { } catch (err) { const errText = formatErrorMessage(err); if (!NOT_MODIFIED_RE.test(errText)) { - throw err; + if (!shouldFallbackPlain(errText)) { + throw err; + } + const fallbackText = renderPlainText(text); + await requestWithDiag( + () => params.api.editMessageText(chatId, currentMessageId, fallbackText), + "editMessage-plain", + ); } } }; diff --git a/src/telegram/format.ts b/src/telegram/format.ts index e3d7e4c4301e..d7c6452e290c 100644 --- a/src/telegram/format.ts +++ b/src/telegram/format.ts @@ -75,6 +75,19 @@ export function renderTelegramHtmlText( return markdownToTelegramHtml(text, { tableMode: options.tableMode }); } +export function markdownToTelegramPlainText( + markdown: string, + options: { tableMode?: MarkdownTableMode } = {}, +): string { + const ir = markdownToIR(markdown ?? "", { + linkify: true, + headingStyle: "none", + blockquotePrefix: "", + tableMode: options.tableMode, + }); + return ir.text; +} + export function markdownToTelegramChunks( markdown: string, limit: number,