fix(telegram): handle large file getFile errors gracefully

Catch GrammyError when getFile fails for files >20MB (Telegram Bot API limit).
Log warning, skip attachment, but continue processing message text.

- Add FILE_TOO_BIG_RE regex to detect 'file is too big' errors
- Add isFileTooBigError() and isRetryableGetFileError() helpers
- Skip retrying permanent 400 errors (they'll fail every time)
- Log specific warning for file size limit errors
- Return null so message text is still processed

Fixes #18518
This commit is contained in:
Brandon Wise
2026-02-16 15:11:48 -05:00
committed by Peter Steinberger
parent 1953b938e3
commit 01b37f1d32
2 changed files with 102 additions and 1 deletions

View File

@@ -6,7 +6,7 @@ import type { RuntimeEnv } from "../../runtime.js";
import type { TelegramInlineButtons } from "../button-types.js";
import type { StickerMetadata, TelegramContext } from "./types.js";
import { chunkMarkdownTextWithMode, type ChunkMode } from "../../auto-reply/chunk.js";
import { danger, logVerbose } from "../../globals.js";
import { danger, logVerbose, warn } from "../../globals.js";
import { formatErrorMessage } from "../../infra/errors.js";
import { retryAsync } from "../../infra/retry.js";
import { mediaKindFromMime } from "../../media/constants.js";
@@ -34,6 +34,7 @@ import {
const PARSE_ERR_RE = /can't parse entities|parse entities|find end of the entity/i;
const VOICE_FORBIDDEN_RE = /VOICE_MESSAGES_FORBIDDEN/;
const FILE_TOO_BIG_RE = /file is too big/i;
export async function deliverReplies(params: {
replies: ReplyPayload[];
@@ -414,10 +415,20 @@ export async function resolveMedia(
maxDelayMs: 4000,
jitter: 0.2,
label: "telegram:getFile",
shouldRetry: isRetryableGetFileError,
onRetry: ({ attempt, maxAttempts }) =>
logVerbose(`telegram: getFile retry ${attempt}/${maxAttempts}`),
});
} catch (err) {
// Handle "file is too big" separately - Telegram Bot API has a 20MB download limit
if (isFileTooBigError(err)) {
logVerbose(
warn(
"telegram: getFile failed - file exceeds Telegram Bot API 20MB limit; skipping attachment",
),
);
return null;
}
// All retries exhausted — return null so the message still reaches the agent
// with a type-based placeholder (e.g. <media:audio>) instead of being dropped.
logVerbose(`telegram: getFile failed after retries: ${String(err)}`);
@@ -442,6 +453,31 @@ function isVoiceMessagesForbidden(err: unknown): boolean {
return VOICE_FORBIDDEN_RE.test(formatErrorMessage(err));
}
/**
* Returns true if the error is Telegram's "file is too big" error.
* This happens when trying to download files >20MB via the Bot API.
* Unlike network errors, this is a permanent error and should not be retried.
*/
function isFileTooBigError(err: unknown): boolean {
if (err instanceof GrammyError) {
return FILE_TOO_BIG_RE.test(err.description);
}
return FILE_TOO_BIG_RE.test(formatErrorMessage(err));
}
/**
* Returns true if the error is a transient network error that should be retried.
* Returns false for permanent errors like "file is too big" (400 Bad Request).
*/
function isRetryableGetFileError(err: unknown): boolean {
// Don't retry "file is too big" - it's a permanent 400 error
if (isFileTooBigError(err)) {
return false;
}
// Retry all other errors (network issues, timeouts, etc.)
return true;
}
async function sendTelegramVoiceFallbackText(opts: {
bot: Bot;
chatId: string;