Puppeteer DeviceRequestPrompt: Select and Connect Devices
Learn how to catch Puppeteer’s device prompt, match the intended device, select it, or cancel safely—with complete code and troubleshooting.
Puppeteer’s DeviceRequestPrompt lets your script respond when a page requests a device, for example through Web Bluetooth. Start page.waitForDevicePrompt() before the page action that triggers the request, find the intended entry with prompt.waitForDevice(predicate), then pass that entry to prompt.select(device). Call prompt.cancel() when the request should be declined. The ordering matters: the wait does not return a prompt that is already active. Puppeteer DeviceRequestPrompt API · waitForDevicePrompt API.
How to select a device with Puppeteer
- Load the page that can request a device.
- Register
waitForDevicePrompt()before clicking the page control that opens the device chooser. - Wait for a matching device with a predicate.
- Select the returned entry, or cancel the prompt if no suitable device is available.
The documented pattern starts the prompt wait and click together with Promise.all. This prevents the click from triggering the request before Puppeteer begins waiting.
const [devicePrompt] = await Promise.all([
page.waitForDevicePrompt(),
page.click('#connect-bluetooth'),
]);
const device = await devicePrompt.waitForDevice(({ name }) =>
name.includes('My Device'),
);
await devicePrompt.select(device);
Replace the selector with the page’s actual connect control and the name condition with a predicate that fits your application. The example follows Puppeteer’s documented flow; it assumes the page exposes a control that triggers the request.
Complete runnable example
This Node.js example launches Puppeteer, navigates to a page you control, arms the device-prompt wait before clicking, and selects the first device whose name matches. Install Puppeteer in your project with npm install puppeteer, save the script as select-device.js, and run node select-device.js. Set PAGE_URL to a page that actually requests a device and DEVICE_NAME to a distinguishing name visible to the prompt.
const puppeteer = require('puppeteer');
async function main() {
const pageUrl = process.env.PAGE_URL;
const deviceName = process.env.DEVICE_NAME || 'My Device';
if (!pageUrl) throw new Error('Set PAGE_URL to your device-request page');
const browser = await puppeteer.launch({ headless: false });
try {
const page = await browser.newPage();
await page.goto(pageUrl, { waitUntil: 'domcontentloaded' });
const [prompt] = await Promise.all([
page.waitForDevicePrompt({ timeout: 15000 }),
page.click('#connect-bluetooth'),
]);
console.log('Selectable entries:', prompt.devices);
const device = await prompt.waitForDevice(
({ name }) => name.includes(deviceName),
{ timeout: 15000 },
);
await prompt.select(device);
console.log('Selected device:', device);
} finally {
await browser.close();
}
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Device availability depends on the page, browser, operating system, permissions, and test environment. The Puppeteer API reference does not prescribe a particular hardware model. Use a device and environment appropriate to the application you are automating.
DeviceRequestPrompt API at a glance
| API | Purpose | Notes |
|---|---|---|
page.waitForDevicePrompt(options?) |
Wait for an upcoming device request. | Call before causing the page request; it will not return an already-active prompt. |
prompt.devices |
Inspect the current selectable entries. | Read-only list; useful for diagnosing a predicate that does not match. |
prompt.waitForDevice(filter, options?) |
Wait for the first entry matching a predicate. | The filter receives a DeviceRequestPromptDevice; timeout options are optional. |
prompt.select(device) |
Choose an entry in this prompt. | Pass the matching prompt entry returned by the wait or taken from this prompt’s list. |
prompt.cancel() |
Dismiss the request without selecting. | Use for an intentional decline or a no-match policy. |
See the class reference and waitForDevice reference for the installed-version signatures. The filter is a predicate over a device entry and the method resolves to the first match.
Choosing a reliable device match
Match only on information your page and test setup can distinguish. A name substring is convenient for a small lab, but can select the wrong entry when multiple devices share similar names. Inspect prompt.devices while developing, then make the predicate as specific as the available device properties allow. The API reference confirms a predicate-based filter but does not prescribe a universal matching strategy.
- Prefer a stable, distinctive condition over a broad match.
- Account for no matching entry: let the wait time out or implement a deliberate cancellation path.
- Do not start the page action before the prompt wait is registered.
- Use explicit timeouts appropriate to your environment; slower discovery may need more time than a local test.
Timeouts, cancellation, and edge cases
There are two separate waits in the common flow: waiting for the prompt and waiting for a matching device entry. Their timeout options can be set independently. A prompt may arrive while its device list is empty or before the desired entry appears, so waiting for the predicate is more robust than immediately indexing prompt.devices.
const prompt = await page.waitForDevicePrompt({ timeout: 15000 });
try {
const device = await prompt.waitForDevice(
({ name }) => name === 'Lab Sensor A',
{ timeout: 10000 },
);
await prompt.select(device);
} catch (error) {
await prompt.cancel();
throw error;
}
This variant assumes the prompt wait was already armed before the action that caused the request; in a real flow, keep the wait and triggering action paired as shown earlier. Cancellation is useful when the application should decline on timeout. If the script should fail visibly instead, cancel and rethrow as above.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
waitForDevicePrompt() times out |
The request action did not run, the wrong control was clicked, or the wait started after the prompt. | Verify the page selector and arm the wait before clicking. Check that the page reaches the code path that requests a device. |
waitForDevice() times out |
No available entry satisfies the predicate, or device discovery has not completed. | Log prompt.devices, confirm the expected device is available, and refine the predicate or allow an appropriate timeout. |
| The wrong device is selected | The predicate matches multiple similarly named entries. | Use a more discriminating predicate based on available entry properties and the test setup. |
| The prompt is never handled | The browser or environment does not expose a request under the current page and test conditions. | Check the page’s device-request flow, browser permissions, and whether the test environment supports the application’s device API. |
| Selection rejects or has no expected effect | The entry is stale, belongs to another prompt, or the page’s connection flow has another failure. | Select an entry from the active prompt, capture the error, and inspect the page’s follow-up behavior. |
Performance, reliability, and cost
This workflow is event-driven: wait for the prompt and matching entry instead of repeatedly polling. Prompt and device timeouts determine how long a failed or slow request can hold the automation open. Keep them bounded, choose values based on the environment, and log the prompt’s device list when a match fails. Hardware discovery timing is environment-dependent, so a timeout that works locally may be too short on a remote or slower test machine.
For repeatable automation, make device identity and the no-match behavior explicit. Close the browser in a finally block so failures do not leave the process running. The dossier documents no benchmark, universal discovery time, or hardware requirement; measure timing in the environment you deploy.
Or skip the browser setup
If your task is capturing a page rather than exercising its device chooser, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. It removes cookie banners, newsletter popups, and chat widgets before the shot; bot checks, blank pages, and failed loads are never billed. AI agents can take screenshots through its MCP server, and the free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. See 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
Sign up for ScreenshotNeo’s free plan: 1,000 screenshots a month, no card required.
FAQ
Can Puppeteer select a device prompt that is already open?
No. The documented method must be called before the device request and does not return a currently active prompt.
Does Puppeteer require a specific Bluetooth device?
The API documentation does not name a required model. The hardware and test setup depend on the page and application being automated.
Can I cancel instead of selecting?
Yes. Call prompt.cancel() to dismiss the prompt without choosing an entry.
Which Puppeteer version should I use?
Use the API signatures documented for the Puppeteer version installed in your project; the references can change across versions.


