ScreenshotNeo

BlogGuides

Puppeteer Device Request Prompt Devices Explained

Learn what Puppeteer’s DeviceRequestPrompt exposes, how to wait for a Web Bluetooth request, find a device, select it, or cancel the prompt.

By the ScreenshotNeo team4 October 20268 min read

DeviceRequestPrompt is Puppeteer’s handle for responding to a page that requests a device through an API such as Web Bluetooth. Get it with page.waitForDevicePrompt(), which resolves with a prompt containing the currently selectable devices. Find the device you want with waitForDevice(), then pass that device to select(); call cancel() to dismiss the request instead. The key timing rule: start waiting before the page action that opens the prompt. Puppeteer’s API reference describes the prompt and its methods.

What is a Puppeteer device request prompt?

A device request prompt is the browser’s response to a page asking to connect to a device. For example, a page might call the Web Bluetooth API after a visitor clicks a connect button. Puppeteer’s DeviceRequestPrompt lets automation inspect the devices offered for that request, select one, or cancel.

The prompt is not a device emulator. Puppeteer’s KnownDevices and Page.emulate() concern browser emulation, a separate feature. A device request prompt handles the page’s request and the selection flow. See the DeviceRequestPrompt reference.

What does prompt.devices contain?

prompt.devices is the current list of selectable devices exposed by the prompt. Each device has an id and a name, where the name is the one shown in the prompt. Use the properties to identify a match; pass the matching device object itself to prompt.select(device).

Do not assume that a particular device will always be present, that names are unique, or that the list is fixed. The available entries depend on what the browser surfaces for the request. Match with the most specific information your test can rely on, and handle the case where no device matches.

Complete Puppeteer example: wait, match, and select

This runnable Node.js example starts listening before clicking the page’s connect button, waits for a device whose name contains the configured text, and selects it. It expects a page at PAGE_URL with a button matching #connect-bluetooth that triggers a device request. Replace those values with your test page’s URL and selector.

import puppeteer from 'puppeteer';

const PAGE_URL = process.env.PAGE_URL ?? 'http://localhost:3000';
const DEVICE_NAME_PART = process.env.DEVICE_NAME_PART ?? 'My Device';

const browser = await puppeteer.launch({headless: false});
try {
  const page = await browser.newPage();
  await page.goto(PAGE_URL, {waitUntil: 'domcontentloaded'});

  // Register the prompt wait before the click that triggers the request.
  const [prompt] = await Promise.all([
    page.waitForDevicePrompt({timeout: 30_000}),
    page.click('#connect-bluetooth'),
  ]);

  console.log('Selectable devices:', prompt.devices);
  const device = await prompt.waitForDevice(
    ({name}) => name.includes(DEVICE_NAME_PART),
    {timeout: 15_000},
  );

  await prompt.select(device);
  console.log(`Selected device: ${device.name} (${device.id})`);
} finally {
  await browser.close();
}

Run it with Puppeteer installed and your test page available:

npm install puppeteer
PAGE_URL=http://localhost:3000 DEVICE_NAME_PART="My Device" node device-prompt.mjs

The example uses a visible browser so the flow is easier to inspect. Whether a Bluetooth request can succeed also depends on the browser, its environment, permissions, and access to devices; the prompt API does not create hardware or grant a page permission by itself.

Why the wait must start before the click

waitForDevicePrompt() does not return a prompt that is already active. Start its promise before the user action or scripted interaction that triggers the request. Promise.all() is useful because it registers the wait and performs the click together:

const [prompt] = await Promise.all([
  page.waitForDevicePrompt(),
  page.click('#connect-bluetooth'),
]);

Calling the methods sequentially in the opposite order can miss the prompt:

// Risky: the click may open the prompt before Puppeteer starts waiting.
await page.click('#connect-bluetooth');
const prompt = await page.waitForDevicePrompt();

The page method accepts wait options. Its default timeout is 30 seconds; set timeout to another number in milliseconds, or 0 to wait without a timeout. A signal can also cancel the wait. See Page.waitForDevicePrompt() and the WaitTimeoutOptions reference.

Match devices reliably

Inspect the current list

When a prompt arrives, log or inspect prompt.devices to see which entries Puppeteer can select. Avoid printing device identifiers in shared logs if those values are sensitive in your environment.

for (const device of prompt.devices) {
  console.log({id: device.id, name: device.name});
}

Wait for a match that may arrive later

Use waitForDevice(filter, options?) when the desired entry might not be in the list immediately. The filter receives a device and should return true for the one to select. This method resolves with the first matching device or rejects if its wait times out. Its optional wait settings support a timeout and abort signal. See DeviceRequestPrompt.waitForDevice().

const device = await prompt.waitForDevice(
  device => device.name === 'Lab Sensor',
  {timeout: 10_000},
);
await prompt.select(device);

Use an exact name if it is stable and unique in your test setup. If names vary, use a carefully chosen predicate and verify that it cannot match an unintended entry. The prompt API’s documented device properties are id and name; do not build a filter around undocumented fields.

Cancel the prompt

If the expected device is unavailable or the test should verify the dismissed state, cancel the pending request with prompt.cancel():

const [prompt] = await Promise.all([
  page.waitForDevicePrompt(),
  page.click('#connect-bluetooth'),
]);

if (prompt.devices.length === 0) {
  await prompt.cancel();
  console.log('Cancelled an empty device prompt');
}

Cancellation dismisses the request; it does not select or connect a device. If a matching device is expected, treat an empty list or a timed-out match as a test condition to diagnose rather than silently reporting success.

Options and error handling

Call What it does Useful options
page.waitForDevicePrompt(options?) Waits for a new device request prompt. timeout in milliseconds; 0 disables the timeout. Supports an abort signal.
prompt.waitForDevice(filter, options?) Waits for the first listed device that passes the filter. Filter callback plus wait timeout and abort signal.
prompt.select(device) Selects a device from the prompt’s list. Pass a device object from the prompt, typically the result of waitForDevice().
prompt.cancel() Cancels the pending prompt. No extra options documented.

Both wait calls can reject when their timeout expires. Handle failures at the appropriate level in your test runner, and close the browser in a finally block so a rejected wait does not leave a process running. Setting a wait timeout to zero can leave automation waiting indefinitely if the page never triggers a request or the target device never appears.

Common problems and fixes

Symptom Likely cause Fix
waitForDevicePrompt() times out. The click did not trigger a device request, the selector did not activate the intended code path, or the wait began after the prompt was already active. Start waiting and clicking together with Promise.all(). Confirm the selector and page behavior, and increase the timeout only if the request legitimately takes longer.
The prompt arrives but no device matches. The expected device is absent, the name differs, or the filter is too strict. Inspect prompt.devices, verify the expected name, and use a predicate that matches the intended device without matching others.
waitForDevice() times out. No device satisfied its filter before the wait deadline. Check the displayed names and device availability; use an explicit timeout appropriate to the test and fail or cancel deliberately.
select(device) rejects. The device object is not one currently offered by that prompt, or the prompt is no longer pending. Use the object returned by that prompt’s waitForDevice() and select it while the request is still active.
The request behaves differently in a browser or CI environment. Device access and browser support depend on the environment; the test may not have access to a real or available device. Check the current Puppeteer and browser support for the environment, permissions, and hardware. The Puppeteer repository’s test expectations mark these tests skipped for Firefox, which is a test-suite signal, not a complete browser support matrix.
The page says it cannot access Bluetooth. The issue may be in the page’s Web Bluetooth prerequisites or browser permissions rather than Puppeteer’s prompt handle. Diagnose the page API call and browser environment separately, then confirm that the action actually requests a device before waiting for the prompt.

Compatibility, reliability, and performance

The prompt is tied to the browser’s device request flow, so tests that depend on physical or discoverable devices can be environment-sensitive. Keep the triggering action and selection in one test flow, use a bounded timeout, and make the no-match outcome explicit. Do not infer broad browser compatibility from a single test configuration: Puppeteer’s repository marks device-request-prompt tests skipped for Firefox, but that alone does not establish a full support matrix. Check current Puppeteer and browser support for your exact setup.

Prompt handling itself is a small part of an automation run. The practical delay is usually the time until the page requests a device and the requested device becomes selectable. A long timeout does not make the device appear sooner; it only makes a missing request take longer to report. Avoid disabling timeouts in unattended runs unless another cancellation mechanism guarantees the job can finish.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server from ScreenshotNeo. It captures a URL as an image or PDF; it does not automate Bluetooth device selection. If the task is to capture the page rather than test its hardware interaction, one request is enough. 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
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
  • Cookie banners, newsletter popups, and chat widgets are removed before the shot.
  • Bot checks, blank pages, and failed loads are never billed.
  • An MCP server lets AI agents use screenshot tools.
  • The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.

Sign up free for 1,000 screenshots a month, no card required.

FAQ

Does DeviceRequestPrompt connect to Bluetooth by itself?

No. It lets Puppeteer respond to a page’s device request by selecting a listed device or cancelling the prompt. The page and browser still need to support and initiate the request.

Can I select a device by its ID?

The prompt device exposes an id and a name, but selection takes the device object. Find the entry in the prompt’s list and pass that object to select().

Is this the same as Puppeteer device emulation?

No. Device request prompts handle a page’s request to choose a device. Puppeteer’s KnownDevices and Page.emulate() are separate browser emulation functionality.

Does ScreenshotNeo replace this API?

No. ScreenshotNeo captures web pages as images or PDFs; it does not handle device prompts or Bluetooth selection.