Skip to content

Conversation

@seanmcguire12
Copy link
Member

@seanmcguire12 seanmcguire12 commented Nov 14, 2025

why

  • file uploads were failing on Browserbase. this is because setInputFiles() was not serializing the file payload

what changed

changed business logic of setInputFiles() to:

  • call normalizeInputFiles(), which returns a serializable/base64 encoded file
  • check if the browser is remote
  • if remote, assignFilesViaPayloadInjection() is called, which then calls assignFilePayloadsToInputElement() (browser-side JS) and reconstructs the base64 back into a file, and directly assigns the reconstructed files to the input element
  • if not remote (local), then the files are written to temp dirs, and those dirs are passed into DOM.setInputFiles (CDP command)

test plan

  • added additional tests for various file input shapes
  • regression evals

@changeset-bot
Copy link

changeset-bot bot commented Nov 14, 2025

🦋 Changeset detected

Latest commit: 6e1378c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@browserbasehq/stagehand Patch
@browserbasehq/stagehand-evals Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@seanmcguire12 seanmcguire12 marked this pull request as ready for review November 14, 2025 21:57
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

9 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@browserbase browserbase deleted a comment from greptile-apps bot Nov 14, 2025
@seanmcguire12
Copy link
Member Author

@greptileai

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Nov 14, 2025

Greptile Overview

Greptile Summary

This PR fixes file uploads on Browserbase by implementing a dual-path strategy: remote browsers now serialize files to base64 and reconstruct them client-side via JavaScript injection, while local browsers continue using CDP's DOM.setFileInputFiles.

Key Changes:

  • Created normalizeInputFiles() to standardize file inputs (paths/payloads) into in-memory buffers
  • Added isBrowserRemote() check in Frame to detect Browserbase environments
  • Implemented assignFilePayloadsToInputElement() browser-side script that reconstructs File objects from base64
  • Added 50MB size limit for remote uploads (matching Playwright's limit)
  • Expanded test coverage with fixtures for relative paths, absolute paths, payload objects, iframes, and multiple files

Implementation Quality:

  • Clean separation between normalization logic (fileUploadUtils.ts) and execution paths
  • Proper error handling with meaningful error messages
  • Consistent with existing codebase patterns
  • Good test coverage across different file input shapes

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The implementation is well-structured with clear separation of concerns, comprehensive test coverage, proper error handling, and follows existing patterns. The approach mirrors Playwright's remote file upload strategy (including the 50MB limit), which is battle-tested. The changes are isolated to file upload functionality without affecting other features.
  • No files require special attention

Important Files Changed

File Analysis

Filename Score Overview
packages/core/lib/v3/understudy/fileUploadUtils.ts 5/5 New utility file for normalizing file inputs - handles both file paths and buffer payloads correctly
packages/core/lib/v3/understudy/locator.ts 5/5 Updated setInputFiles to detect remote browsers and use payload injection method for Browserbase
packages/core/lib/v3/dom/locatorScripts/scripts.ts 5/5 Added assignFilePayloadsToInputElement function to reconstruct files from base64 in remote browsers
packages/core/lib/v3/tests/setinputfiles.spec.ts 5/5 Enhanced test coverage with fixtures and validation helpers for various file input scenarios

Sequence Diagram

sequenceDiagram
    participant User
    participant Locator
    participant FileUtils as normalizeInputFiles
    participant Frame
    participant CDP as CDP Session
    participant Browser as Browser/Page

    User->>Locator: setInputFiles(files)
    Locator->>CDP: Validate element is input[type=file]
    CDP-->>Locator: Validation OK
    
    Locator->>FileUtils: normalizeInputFiles(files)
    alt File path provided
        FileUtils->>FileUtils: Resolve absolute path
        FileUtils->>FileUtils: Read file from disk
        FileUtils-->>Locator: NormalizedFilePayload with buffer & path
    else Payload object provided
        FileUtils->>FileUtils: Convert buffer to Node Buffer
        FileUtils-->>Locator: NormalizedFilePayload with buffer
    end
    
    Locator->>Frame: isBrowserRemote()
    
    alt Remote Browser (Browserbase)
        Frame-->>Locator: true
        Locator->>Locator: Check file size < 50MB
        Locator->>Locator: Serialize files to base64
        Locator->>CDP: callFunctionOn(assignFilePayloadsToInputElement)
        CDP->>Browser: Execute in page context
        Browser->>Browser: Decode base64 to bytes
        Browser->>Browser: Create File objects via DataTransfer
        Browser->>Browser: Assign to input.files
        Browser->>Browser: Dispatch input/change events
        Browser-->>CDP: Success
        CDP-->>Locator: Upload complete
    else Local Browser
        Frame-->>Locator: false
        alt File has absolutePath
            Locator->>Locator: Use existing path
        else Payload without path
            Locator->>Locator: Write buffer to temp file
        end
        Locator->>CDP: DOM.setFileInputFiles(filePaths)
        CDP->>Browser: Set files via CDP
        Browser-->>CDP: Success
        CDP-->>Locator: Upload complete
        Locator->>Locator: Cleanup temp files
    end
    
    Locator-->>User: Success
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

9 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

@seanmcguire12 seanmcguire12 merged commit ffb5e5d into main Nov 14, 2025
15 checks passed
seanmcguire12 pushed a commit that referenced this pull request Nov 16, 2025
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @browserbasehq/stagehand@3.0.3

### Patch Changes

- [#1273](#1273)
[`ab51232`](ab51232)
Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - fix:
trigger shadow root rerender in OOPIFs by cloning & replacing instead of
reloading

- [#1268](#1268)
[`c76ade0`](c76ade0)
Thanks [@tkattkat](https://github.com/tkattkat)! - Expose reasoning, and
cached input tokens in stagehand metrics

- [#1267](#1267)
[`ffb5e5d`](ffb5e5d)
Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - fix: file
uploads failing on Browserbase

- [#1269](#1269)
[`772e735`](772e735)
Thanks [@tkattkat](https://github.com/tkattkat)! - Add example using
playwright screen recording

## @browserbasehq/stagehand-evals@1.1.3

### Patch Changes

- Updated dependencies
\[[`ab51232`](ab51232),
[`c76ade0`](c76ade0),
[`ffb5e5d`](ffb5e5d),
[`772e735`](772e735)]:
    -   @browserbasehq/stagehand@3.0.3

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants