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 37 commits intosimstudioai:stagingfrom
Conversation
…keyboard shortcuts, audit logs
…rects to rewrites
…stash, algolia tools; isolated-vm robustness improvements, tables backend (simstudioai#3271) * feat(tools): advanced fields for youtube, vercel; added cloudflare and dataverse tools (simstudioai#3257) * refactor(vercel): mark optional fields as advanced mode Move optional/power-user fields behind the advanced toggle: - List Deployments: project filter, target, state - Create Deployment: project ID override, redeploy from, target - List Projects: search - Create/Update Project: framework, build/output/install commands - Env Vars: variable type - Webhooks: project IDs filter - Checks: path, details URL - Team Members: role filter - All operations: team ID scope Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(youtube): mark optional params as advanced mode Hide pagination, sort order, and filter fields behind the advanced toggle for a cleaner default UX across all YouTube operations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * added advanced fields for vercel and youtube, added cloudflare and dataverse block * addded desc for dataverse * add more tools * ack comment * more * ops --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat(tables): added tables (simstudioai#2867) * updates * required * trashy table viewer * updates * updates * filtering ui * updates * updates * updates * one input mode * format * fix lints * improved errors * updates * updates * chages * doc strings * breaking down file * update comments with ai * updates * comments * changes * revert * updates * dedupe * updates * updates * updates * refactoring * renames & refactors * refactoring * updates * undo * update db * wand * updates * fix comments * fixes * simplify comments * u[dates * renames * better comments * validation * updates * updates * updates * fix sorting * fix appearnce * updating prompt to make it user sort * rm * updates * rename * comments * clean comments * simplicifcaiton * updates * updates * refactor * reduced type confusion * undo * rename * undo changes * undo * simplify * updates * updates * revert * updates * db updates * type fix * fix * fix error handling * updates * docs * docs * updates * rename * dedupe * revert * uncook * updates * fix * fix * fix * fix * prepare merge * readd migrations * add back missed code * migrate enrichment logic to general abstraction * address bugbot concerns * adhere to size limits for tables * remove conflicting migration * add back migrations * fix tables auth * fix permissive auth * fix lint * reran migrations * migrate to use tanstack query for all server state * update table-selector * update names * added tables to permission groups, updated subblock types --------- Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai> Co-authored-by: waleed <walif6@gmail.com> * fix(snapshot): changed insert to upsert when concurrent identical child workflows are running (simstudioai#3259) * fix(snapshot): changed insert to upsert when concurrent identical child workflows are running * fixed ci tests failing * fix(workflows): disallow duplicate workflow names at the same folder level (simstudioai#3260) * feat(tools): added redis, upstash, algolia, and revenuecat (simstudioai#3261) * feat(tools): added redis, upstash, algolia, and revenuecat * ack comment * feat(models): add gemini-3.1-pro-preview and update gemini-3-pro thinking levels (simstudioai#3263) * fix(audit-log): lazily resolve actor name/email when missing (simstudioai#3262) * fix(blocks): move type coercions from tools.config.tool to tools.config.params (simstudioai#3264) * fix(blocks): move type coercions from tools.config.tool to tools.config.params Number() coercions in tools.config.tool ran at serialization time before variable resolution, destroying dynamic references like <block.result.count> by converting them to NaN/null. Moved all coercions to tools.config.params which runs at execution time after variables are resolved. Fixed in 15 blocks: exa, arxiv, sentry, incidentio, wikipedia, ahrefs, posthog, elasticsearch, dropbox, hunter, lemlist, spotify, youtube, grafana, parallel. Also added mode: 'advanced' to optional exa fields. Closes simstudioai#3258 * fix(blocks): address PR review — move remaining param mutations from tool() to params() - Moved field mappings from tool() to params() in grafana, posthog, lemlist, spotify, dropbox (same dynamic reference bug) - Fixed parallel.ts excerpts/full_content boolean logic - Fixed parallel.ts search_queries empty case (must set undefined) - Fixed elasticsearch.ts timeout not included when already ends with 's' - Restored dropbox.ts tool() switch for proper default fallback * fix(blocks): restore field renames to tool() for serialization-time validation Field renames (e.g. personalApiKey→apiKey) must be in tool() because validateRequiredFieldsBeforeExecution calls selectToolId()→tool() then checks renamed field names on params. Only type coercions (Number(), boolean) stay in params() to avoid destroying dynamic variable references. * improvement(resolver): resovled empty sentinel to not pass through unexecuted valid refs to text inputs (simstudioai#3266) * fix(blocks): add required constraint for serviceDeskId in JSM block (simstudioai#3268) * fix(blocks): add required constraint for serviceDeskId in JSM block * fix(blocks): rename custom field values to request field values in JSM create request * fix(trigger): add isolated-vm support to trigger.dev container builds (simstudioai#3269) Scheduled workflow executions running in trigger.dev containers were failing to spawn isolated-vm workers because the native module wasn't available in the container. This caused loop condition evaluation to silently fail and exit after one iteration. - Add isolated-vm to build.external and additionalPackages in trigger config - Include isolated-vm-worker.cjs via additionalFiles for child process spawning - Add fallback path resolution for worker file in trigger.dev environment * fix(tables): hide tables from sidebar and block registry (simstudioai#3270) * fix(tables): hide tables from sidebar and block registry * fix(trigger): add isolated-vm support to trigger.dev container builds (simstudioai#3269) Scheduled workflow executions running in trigger.dev containers were failing to spawn isolated-vm workers because the native module wasn't available in the container. This caused loop condition evaluation to silently fail and exit after one iteration. - Add isolated-vm to build.external and additionalPackages in trigger config - Include isolated-vm-worker.cjs via additionalFiles for child process spawning - Add fallback path resolution for worker file in trigger.dev environment * lint * fix(trigger): update node version to align with main app (simstudioai#3272) * fix(build): fix corrupted sticky disk cache on blacksmith (simstudioai#3273) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Lakee Sivaraya <71339072+lakeesiv@users.noreply.github.com> Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai> Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
… fixes, removed retired models, hex integration
…ogle tasks and bigquery integrations, workflow lock
…gespeed insights, pagerduty
…, brandfetch, google meet
… pagination, memory improvements
… selectors for 14 blocks
…ory instrumentation
…aders, webhook trigger configs (simstudioai#3530)
…anvas navigation updates
… block classifications
v0.6.8: mothership tool loop
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 real user-facing bug where plain Confidence Score: 3/5The core Telegram fix is correct, but the shared utility change has unintended reach across ~15 other blocks and a type-safety gap worth resolving before merge. The intended fix works and tests are thorough. However, modifying a broadly-shared utility function without scoping the change introduces a silent behavioural change for upload-oriented blocks, and the string[] vs object[] overload mismatch is a real type-safety regression. apps/sim/blocks/utils.ts — specifically the non-single overload return type and the opt-in scoping of the URL passthrough. 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 -- yes --> E{trimmed starts with http/https?}
E -- yes --> F{options.single?}
F -- yes --> G[return trimmed URL string]
F -- no --> H[return string array]
E -- no --> I[JSON.parse attempt]
I -- success --> J[fileParam = parsed value]
I -- fail --> K[return undefined]
J --> L{Array.isArray?}
D -- no --> L
L -- yes --> M{length > 0?}
M -- yes --> N[files = fileParam]
M -- no --> O[return undefined]
L -- no --> P{is plain object?}
P -- yes --> Q[files = wrapped in array]
P -- no --> O
N --> R{options.single?}
Q --> R
R -- yes --> S{files.length > 1?}
S -- yes --> T[throw error]
S -- no --> U[return files 0]
R -- no --> V[return files array]
Reviews (1): 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.