How to Check Whether a File Chooser Allows Multiple Files in Puppeteer
Use Puppeteer’s FileChooser.isMultiple() to check whether an opened file chooser accepts multiple files. Here’s how to wait for it safely, handle it, and troubleshoot common issues.
Call isMultiple() on the FileChooser returned by page.waitForFileChooser(). It returns a boolean: true means the chooser permits selecting multiple files; false means it permits only one. Start waiting before you trigger the action that opens the chooser.
const [fileChooser] = await Promise.all([
page.waitForFileChooser(),
page.click('#upload-file-button'),
]);
console.log(fileChooser.isMultiple()); // true or false
This is Puppeteer’s documented pattern for coordinating the chooser wait with the click that opens it. See the FileChooser API and isMultiple() reference.
Complete example
The following CommonJS script launches Chromium, opens a page with a multiple-file input, checks the chooser setting, supplies two files, and closes the browser. Save it as check-chooser.cjs and run it with Node.js after installing Puppeteer and creating the two sample files.
const puppeteer = require('puppeteer');
async function main() {
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.setContent(`
<button id="upload-file-button">Choose files</button>
<input id="files" type="file" multiple hidden>
<script>
document.querySelector('#upload-file-button').addEventListener('click', () => {
document.querySelector('#files').click();
});
</script>
`);
const [fileChooser] = await Promise.all([
page.waitForFileChooser(),
page.click('#upload-file-button'),
]);
const allowsMultipleFiles = fileChooser.isMultiple();
console.log(`Allows multiple files: ${allowsMultipleFiles}`);
if (allowsMultipleFiles) {
await fileChooser.accept([
`${process.cwd()}/fixtures/first.txt`,
`${process.cwd()}/fixtures/second.txt`,
]);
} else {
// A single-file chooser can accept only one path.
await fileChooser.accept([`${process.cwd()}/fixtures/first.txt`]);
}
const selectedCount = await page.$eval('#files', input => input.files.length);
console.log(`Files selected: ${selectedCount}`);
} finally {
await browser.close();
}
}
main().catch(error => {
console.error(error);
process.exitCode = 1;
});
Create fixtures/first.txt and fixtures/second.txt before running the example. The HTML uses a multiple file input, so the check should print true. To see the single-file case, remove the multiple attribute.
How the check works
- Register
page.waitForFileChooser()before the page action that opens the chooser. - Trigger the action, commonly a click, in the same
Promise.allcall. - Read
fileChooser.isMultiple(). It takes no arguments and returns a boolean. - Accept or cancel the chooser so it does not remain open and interfere with a later chooser.
The check describes the opened chooser. Inspecting an input’s markup may tell you whether that element has a multiple attribute, but it is not the same as checking the actual FileChooser instance.
Accepting files and handling paths
If your next step is to populate the chooser, accept() takes an array of file path strings. The array can contain one path or several, depending on the chooser’s configuration. Puppeteer does not check whether the files exist before accepting the paths.
if (fileChooser.isMultiple()) {
await fileChooser.accept(['/absolute/path/one.png', '/absolute/path/two.png']);
} else {
await fileChooser.accept(['/absolute/path/one.png']);
}
Relative paths are resolved from the current working directory. Absolute paths make that resolution explicit. When Puppeteer connects to a remote Chrome instance, use paths accessible to the remote browser environment; a path that exists only on the client machine may not be available there. See Puppeteer’s accept() documentation.
To dismiss a chooser without selecting files, call cancel(). Handle each chooser that your automation opens: the browser permits only one open chooser at a time, and an unresolved chooser can prevent another from appearing.
Timing and supported dialogs
waitForFileChooser() waits for a chooser opened after the wait is registered. Calling it after the click or other trigger can miss the event and leave the script waiting. Coordinate the wait and action as shown above. If the page opens the chooser in response to a different action, replace the click with that action while keeping the wait registered first.
Puppeteer documents that interception of file dialogs opened through DOM APIs such as window.showOpenFilePicker() is not supported. The FileChooser flow applies to supported file chooser dialogs. In headful mode, Puppeteer handles the chooser without displaying the native picker to the user.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| The script hangs waiting for a chooser | The wait was registered after the triggering action, or the action did not open a supported chooser. | Start waitForFileChooser() before the click or other trigger. Confirm the page action actually opens a file chooser. |
| The next chooser does not appear | An earlier chooser is still open. | Resolve every chooser by calling accept() or cancel() before opening another. |
accept() does not select the expected files |
A path is wrong or unavailable in the browser’s environment. | Check that each file exists and use absolute paths. With remote Chrome, make the files available to the remote browser. |
| A multi-file dialog cannot be intercepted | The page may use window.showOpenFilePicker(), which Puppeteer documents as unsupported for this interception flow. |
Use an upload interaction supported by Puppeteer’s file chooser API, or adjust the application’s test seam if you control the page. |
The result is false unexpectedly |
The actual opened chooser allows one file, regardless of what another element or expected page state suggests. | Check the exact control that triggered the chooser and read isMultiple() on that chooser instance. |
Performance, reliability, and cost
isMultiple() is a local boolean query on the chooser object; it does not need a separate page evaluation or network request. The main reliability concern is event timing: install the wait before the trigger, then always resolve the chooser. For remote browser runs, account for where the files reside, since paths must be usable by the browser environment.
This check does not itself upload a file to a server or incur a Puppeteer-specific per-call charge. Any browser hosting, CI, or application costs depend on the environment you run and are outside the chooser API.
Or skip the browser setup
If your goal is to get a clean screenshot of a page rather than automate its file upload, ScreenshotNeo provides a website screenshot API and MCP server. A single request captures a URL; this does not inspect or operate a file chooser.
Example request, following the ScreenshotNeo API documentation:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed. Its MCP server lets AI agents use screenshot tools. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.
Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.
FAQ
What does isMultiple() return?
A boolean: true when the chooser allows multiple selections and false otherwise.
Does calling isMultiple() open the chooser?
No. Obtain a FileChooser from waitForFileChooser() after arranging an action that opens it, then call the method on that object.
Can I use this with every browser file picker?
No. Puppeteer documents limitations for dialogs opened with DOM APIs such as window.showOpenFilePicker().


