Skip to content

[Bug] File upload impossible on Telegram Web via MCP upload tool: SPA creates transient <input type=file> with no persistent DOM ref #2156

Description

@linfeng961

Issue Type

  • Agent Issue
  • Browser Issue

Operating System

  • Windows

Description of the bug

On Telegram Web (https://web.telegram.org/a/), the MCP upload tool cannot upload files through BrowserOS neo — because the tool's ref-based injection contract does not match SPA sites with transient file inputs:

The upload MCP tool requires a ref of an <input type="file"> element from the last snapshot, then calls DOM.setFileInputFiles (per the official implementation: packages/browseros-agent/packages/browser-mcp/src/tools/upload.ts and crates/browseros-mcp/src/tools/upload.rs). But Telegram Web creates the file input transiently: a MutationObserver attached to document.body (subtree) recorded zero input[type=file] nodes during the click, document.querySelectorAll('input[type=file]') stays empty immediately after the File menu item is clicked, and window.showOpenFilePicker was not invoked. So there is never a persistent DOM node to hand to upload as a ref — the input either never becomes a persistent node or is created and destroyed within the same microtask.

Net effect: on this site the MCP upload tool has no working path to inject a file.

Steps to Reproduce

  1. Start BrowserOS neo (fresh profile, Windows, Chromium 148.0.7988.97).
  2. Open a new tab via MCP: tabs action="new" url="https://web.telegram.org/a/".
  3. Open any chat, click the paperclip (attachment) button, then click File in the menu.
  4. Observe: the menu closes, and document.querySelectorAll('input[type=file]') returns 0 — no file input exists in the DOM to target.
  5. Try upload(page, ref, files) — there is no ref available in the snapshot to pass (upload fails with "Node is not a file input element" or cannot resolve a ref).

Screenshots / Videos

N/A (the DOM evidence is described above).

BrowserOS Version

Chromium 148.0.7988.97 (BrowserOS neo / BrowserClaw build on Windows), MCP endpoint http://127.0.0.1:9010/mcp

Additional Context

  • The native file chooser does open on agent clicks. When the File menu item is clicked via MCP (act), a Windows-native "Open" dialog does appear (verified by enumerating top-level windows — it shows up as a separate window with the title "打开"/Open at the top z-order). It is not intercepted or suppressed by BrowserOS: source search confirms there is no Page.setInterceptFileChooserDialog call anywhere in the codebase, and the FILE_CHOOSER timeout constant (packages/browseros-agent/packages/shared/src/constants/timeouts.ts) is defined but never referenced. The only reason this was initially missed is that CDP page screenshots cannot capture OS-level dialogs.
  • Manual (human) clicking works normally: a real user clicking the paperclip → File gets the native Windows file picker every time. The limitation is specific to the MCP upload tool's ref-based injection.
  • Workaround that works today: since the native chooser opens, a desktop-level automation layer (e.g. UIA) can drive the dialog (select the file, click Open) and the upload completes. This proves the browser/transport has no upload blockage — it is purely a missing agent-native injection path.
  • The upload tool's own description says "Set local file path(s) on a file input using a ref from the last snapshot" — that contract breaks on sites that mount the file input only for the duration of the click, or that prefer the File System Access API (showOpenFilePicker, which is available in Chromium 148 and does not create a DOM input at all).
  • Suggestion for a fix (Playwright/Puppeteer pattern): implement Page.setInterceptFileChooserDialog({enabled: true}) before triggering the click, listen for Page.fileChooserOpened (whose payload carries the transient input's backendNodeId), and let upload accept either a ref or the chooser's backendNodeId — i.e. support a "no-ref upload" mode that injects via DOM.setFileInputFiles(files, backendNodeId). This is exactly how Playwright's setInputFiles and Puppeteer's waitForFileChooser handle SPA uploads, and it works regardless of whether the input ever persists in the DOM or AX tree.
  • Related: Does it support file uploading? #11 (older "does it support file uploading?" discussion, closed before the upload tool existed).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions