fix(telegram): allow plain URLs in photo/video/audio/animation fields#3797
fix(telegram): allow plain URLs in photo/video/audio/animation fields#3797cyphercodes wants to merge 1 commit intosimstudioai:stagingfrom
Conversation
Fixes simstudioai#3220 The normalizeFileInput function was rejecting plain URL strings for Telegram media blocks (send_photo, send_video, etc.) because it only accepted JSON stringified file objects. Now it detects http/https URLs and passes them through unchanged, allowing users to provide direct image URLs like "https://example.com/photo.jpg". Changes: - Updated normalizeFileInput to detect and return URL strings as-is - Updated function return types to include string type - Added comprehensive unit tests for URL handling Tested with: - Plain HTTPS URLs - Plain HTTP URLs - URLs with whitespace (trimmed) - JSON stringified file objects (still work) - Regular file objects (still work)
|
@cyphercodes is attempting to deploy a commit to the Sim Team on Vercel. A member of the Team first needs to authorize it. |
PR SummaryMedium Risk Overview Updates the function’s overload return types to include Written by Cursor Bugbot for commit aa0060e. This will update automatically on new commits. Configure here. |
Greptile SummaryThis PR fixes a bug where the Telegram Send Photo/Video/Audio/Animation blocks rejected plain Key changes:
Findings:
Confidence Score: 5/5Safe to merge; the fix correctly unblocks plain URL inputs for Telegram media blocks with no regression risk to existing file-object paths. All remaining findings are P2 style/convention issues (relative import, missing Vitest environment annotation) that do not affect runtime behaviour or correctness. The logic change is well-targeted, backward-compatible, and covered by new tests. apps/sim/blocks/utils.test.ts — minor convention fixes needed (absolute import, @vitest-environment node). Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[normalizeFileInput called] --> B{fileParam falsy?}
B -- yes --> C[return undefined]
B -- no --> D{typeof fileParam === 'string'?}
D -- no --> G
D -- yes --> E{starts with http:// or https://?}
E -- yes --> F{options.single?}
F -- yes --> F1[return trimmed URL string]
F -- no --> F2[return trimmed URL wrapped in array]
E -- no --> J{JSON.parse succeeds?}
J -- no --> K[return undefined]
J -- yes --> G{Array.isArray fileParam?}
G -- yes --> H{length > 0?}
H -- yes --> I[files = fileParam]
H -- no --> K2[files = undefined]
I --> L{options.single?}
K2 --> M[return undefined]
G -- no --> N{typeof object and not null?}
N -- yes --> O[files = fileParam wrapped in array]
N -- no --> M
O --> L
L -- yes --> P{files.length > 1?}
P -- yes --> Q[throw error]
P -- no --> R[return files 0]
L -- no --> S[return files array]
Reviews (2): Last reviewed commit: "fix(telegram): allow plain URLs in photo..." | Re-trigger Greptile |
| export function normalizeFileInput( | ||
| fileParam: unknown, | ||
| options?: { single?: false } | ||
| ): object[] | undefined | ||
| ): object[] | string | undefined |
There was a problem hiding this comment.
Non-single overload return type doesn't include
string[]
When a URL is passed with single: false (or no options), the implementation returns [trimmed] — a string[]. However, the non-single overload declares object[] | string | undefined as its return type. TypeScript doesn't flag this because string[] is assignable to the implementation's object variant (arrays are objects), but it means callers receive a typed promise of object[] while actually getting string[] at runtime.
Any caller that iterates over the returned array and accesses file-object properties (.name, .url, etc.) would silently get undefined instead of failing loudly with a type error. The telegram_send_document path (normalizeFileInput(params.files)) is one such non-single caller — a URL string passed as params.files would return [url] typed as object[] | string | undefined, which could confuse downstream handlers.
The overload should reflect the actual runtime shape:
export function normalizeFileInput(
fileParam: unknown,
options?: { single?: false }
): (object | string)[] | string | undefined| const trimmed = fileParam.trim() | ||
| if (trimmed.startsWith('http://') || trimmed.startsWith('https://')) { | ||
| return options?.single ? trimmed : [trimmed] | ||
| } |
There was a problem hiding this comment.
URL passthrough affects all
normalizeFileInput callers, not just Telegram
This change is correct for Telegram media blocks, but normalizeFileInput is used by ~15 other blocks (Box, Confluence, Google Drive, Fireflies, Jira, Linear, etc.). Previously, passing a URL string to any of these would silently return undefined, causing a validation error. Now the URL string is returned to the caller.
For upload-oriented blocks (e.g. Box's upload_file, Confluence attachments), the caller assigns the result directly to params.file / baseParams.file and passes it to the tool handler. A URL string where those handlers expect a file object with { name, url, size } will likely cause a confusing downstream error instead of the current clear validation failure.
Consider scoping this change to Telegram-only, for example by adding an allowUrl?: boolean option to normalizeFileInput, so the URL shortcut doesn't silently change behaviour for other integrations:
export function normalizeFileInput(
fileParam: unknown,
options: { single: true; allowUrl?: boolean; errorMessage?: string }
): object | string | undefinedThere was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
| fileParam: unknown, | ||
| options?: { single?: false } | ||
| ): object[] | undefined | ||
| ): object[] | string | undefined |
There was a problem hiding this comment.
Non-single overload return type doesn't match actual return
Medium Severity
When single is falsy, the URL path returns [trimmed] which is string[], but the non-single overload declares the return type as object[] | string | undefined. Since string is a primitive and not assignable to object, string[] is neither object[] nor string. A caller using this overload that checks typeof result === 'string' to detect a URL will never match (because it's actually an array), and a caller narrowing to object[] will get string elements incorrectly typed as object, potentially leading to runtime property-access errors on the array items.


Summary
Fixes #3220
The Telegram Send Photo block (and other media blocks) was rejecting valid photo URLs with "Photo is required." error. This happened because the function only accepted JSON-stringified file objects, not plain URLs.
Problem
When users passed a plain URL like to the photo field, the function tried to parse it as JSON, failed, and returned - causing the validation error.
Solution
Updated to detect http/https URLs and pass them through unchanged:
Testing
Impact
This fix affects all Telegram media operations that use :
Users can now use direct URLs from previous blocks (e.g., Function block output) without workarounds.