From 13510d7299dd18c312645aac22baaec6dcee87d1 Mon Sep 17 00:00:00 2001 From: Maxwell Calkin Date: Sun, 8 Mar 2026 21:19:53 -0400 Subject: [PATCH] fix: pass URL strings and file_ids through to Telegram media API The telegram_send_photo, telegram_send_video, telegram_send_audio, and telegram_send_animation blocks always routed media params through normalizeFileInput(), which JSON.parse's string inputs and returns undefined when parsing fails. This caused plain URL strings and Telegram file_id strings (both valid per the Telegram Bot API) to be silently dropped, resulting in "Photo is required." even when a valid URL was provided. Extract a resolveTelegramMedia() helper that passes plain strings through directly and only delegates to normalizeFileInput when the value is a serialized JSON object/array or a non-string value. Fixes #3220 Co-Authored-By: Claude Opus 4.6 --- apps/sim/blocks/blocks/telegram.ts | 41 ++++++++++++++++++------------ 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/apps/sim/blocks/blocks/telegram.ts b/apps/sim/blocks/blocks/telegram.ts index ce4076d3849..daaa3ee7016 100644 --- a/apps/sim/blocks/blocks/telegram.ts +++ b/apps/sim/blocks/blocks/telegram.ts @@ -5,6 +5,27 @@ import { normalizeFileInput } from '@/blocks/utils' import type { TelegramResponse } from '@/tools/telegram/types' import { getTrigger } from '@/triggers' +/** + * Resolves a Telegram media parameter to a value suitable for the Telegram API. + * Telegram accepts plain URL strings and file_id strings directly, but + * normalizeFileInput drops plain strings that aren't valid JSON. This helper + * passes plain strings through and delegates to normalizeFileInput only when + * the value looks like a serialized file object (JSON) or is already an object. + */ +function resolveTelegramMedia(param: unknown): string | object | undefined { + if (typeof param === 'string') { + const trimmed = param.trim() + if (!trimmed) return undefined + // If the string looks like serialized JSON (object or array), let normalizeFileInput parse it + if (trimmed.startsWith('{') || trimmed.startsWith('[')) { + return normalizeFileInput(param, { single: true }) + } + // Plain URL or file_id — pass through directly + return trimmed + } + return normalizeFileInput(param, { single: true }) +} + export const TelegramBlock: BlockConfig = { type: 'telegram', name: 'Telegram', @@ -269,10 +290,7 @@ export const TelegramBlock: BlockConfig = { messageId: params.messageId, } case 'telegram_send_photo': { - // photo is the canonical param for both basic (photoFile) and advanced modes - const photoSource = normalizeFileInput(params.photo, { - single: true, - }) + const photoSource = resolveTelegramMedia(params.photo) if (!photoSource) { throw new Error('Photo is required.') } @@ -283,10 +301,7 @@ export const TelegramBlock: BlockConfig = { } } case 'telegram_send_video': { - // video is the canonical param for both basic (videoFile) and advanced modes - const videoSource = normalizeFileInput(params.video, { - single: true, - }) + const videoSource = resolveTelegramMedia(params.video) if (!videoSource) { throw new Error('Video is required.') } @@ -297,10 +312,7 @@ export const TelegramBlock: BlockConfig = { } } case 'telegram_send_audio': { - // audio is the canonical param for both basic (audioFile) and advanced modes - const audioSource = normalizeFileInput(params.audio, { - single: true, - }) + const audioSource = resolveTelegramMedia(params.audio) if (!audioSource) { throw new Error('Audio is required.') } @@ -311,10 +323,7 @@ export const TelegramBlock: BlockConfig = { } } case 'telegram_send_animation': { - // animation is the canonical param for both basic (animationFile) and advanced modes - const animationSource = normalizeFileInput(params.animation, { - single: true, - }) + const animationSource = resolveTelegramMedia(params.animation) if (!animationSource) { throw new Error('Animation is required.') }