What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Arm the event wait before the action that should emit it, keep the promise unawaited while the action runs, and then await the result. If it still times out, verify the event name and scope, predicate, timeout category, and page or browser-context lifecycle. A timeout alone does not identify which of those is wrong.
The reliable pattern: register first, trigger second
page.waitForEvent() waits for a named page event and resolves with that event’s data. It can also filter the event with a predicate and apply a timeout. The essential ordering is:
- Create the wait promise without awaiting it.
- Run the click, submit, keyboard action, or other operation that should emit the event.
- Await the promise after the action has completed.
const popupPromise = page.waitForEvent('popup');
await page.getByRole('button', { name: 'Open window' }).click();
const popup = await popupPromise;
If you write await page.waitForEvent('popup') before the click, JavaScript pauses at the wait and never reaches the action that would create the popup. The same pattern applies to downloads:
const downloadPromise = page.waitForEvent('download');
await page.getByText('Download file').click();
const download = await downloadPromise;
These patterns are shown in the official Page API, Pages guide, and Downloads guide. The next downloads URL is a documentation path; check the guide that matches the Playwright version installed in your project.
#1 Best Overall
What a waitForEvent failure actually means
A timeout means that no event satisfying your wait arrived before the configured deadline. It does not prove that the application failed, that the browser is slow, or that increasing the timeout is the answer. Work through the following checks in order.
1. Confirm the action really emits that event
Match the event to the browser behavior:
popup: a new popup associated with a particular page.download: a response handled as a download attachment.pageon a browser context: a new page created in that context.- Other page events: use the event documented for the behavior you are observing, rather than a similarly named event.
A button that navigates the current tab does not emit popup. A client-side export that builds a Blob may not produce the download behavior your test expects. Inspect the application code and browser behavior before changing the wait.
2. Use the correct event source
page.waitForEvent('popup') observes popups opened by that page. If any page in the context may be created, listen on the context instead:
const pagePromise = context.waitForEvent('page');
await page.getByRole('link', { name: 'Open report' }).click();
const reportPage = await pagePromise;
The Pages guide covers page-level popup handling, while the BrowserContext API documents context-level page events. Waiting on the wrong object can remain pending even though a page was created elsewhere.
3. Remember when a popup event becomes observable
Playwright does not necessarily expose a popup event at the exact instant application code calls window.open. The Page API describes the popup as available after navigation to its initial URL reaches the point where the network response starts loading. If your goal is to observe the request itself, use context routing or request events instead of treating page.waitForEvent('popup') as a network observer.
Rank #2
Predicates and timeout settings
Check a predicate that rejects the event
A predicate is evaluated against the event data. The wait resolves only when the predicate accepts it. A predicate that expects the wrong URL, filename, or page property can make a correctly emitted event look missing.
const popupPromise = page.waitForEvent('popup', {
predicate: popup => popup.url().includes('/reports/'),
timeout: 15_000
});
await page.getByRole('button', { name: 'Open report' }).click();
const popup = await popupPromise;
Temporarily remove the predicate or log the event’s values to distinguish “the event never fired” from “the predicate never accepted it.” Keep the predicate only as strict as the requirement demands.
Identify which timeout failed
Playwright has separate timeout scopes. A failure may be from:
- the
waitForEventtimeout; - the locator action’s actionability timeout;
- a navigation timeout;
- an assertion timeout;
- the overall test timeout;
- a fixture or global timeout.
The Timeouts guide explains these categories. Read the error text and call log before changing configuration. Increasing an event timeout is reasonable only when the correct event is known to arrive after a legitimate delay. It cannot repair a wrong event name, wrong source object, rejecting predicate, non-emitting action, or closed page.
Page and browser-context lifecycle problems
The Page API states that a pending page event wait throws if the page closes before the event fires. Context waits likewise fail when the browser context closes. Keep the object alive until the event has been handled, and inspect code that closes a page, context, or browser in a fixture cleanup path.
Rank #3
const popupPromise = page.waitForEvent('popup');
await page.getByRole('button', { name: 'Open window' }).click();
const popup = await popupPromise;
await popup.waitForLoadState('domcontentloaded');
// Close only after the event and any required assertions are complete.
Also check that the action is using the same page or context instance on which you installed the listener. Accidentally creating a second page and waiting on the first is a common lifecycle and scope error.
Recommended Free Tools
Dialog handlers can make the triggering action stall
JavaScript alert, confirm, prompt, and beforeunload dialogs need special handling. With no dialog listener, Playwright automatically dismisses dialogs. Once you register page.on('dialog') or a context dialog handler, your handler must call accept() or dismiss(). If it does neither, the action that opened the dialog can remain blocked and the event wait may never reach its expected state.
page.on('dialog', async dialog => {
if (dialog.type() === 'confirm') {
await dialog.accept();
} else {
await dialog.dismiss();
}
});
const popupPromise = page.waitForEvent('popup');
await page.getByRole('button', { name: 'Continue' }).click();
const popup = await popupPromise;
Do not install a handler merely to log dialogs unless it also resolves each dialog. The official Dialogs guide describes this behavior.
Separate actionability failures from event-wait failures
Locator actions auto-wait for actionability checks including uniqueness, visibility, stability, receiving pointer events, and enabled state. If those checks do not pass, the click itself fails with a timeout; that is not the same as an event wait that received no event.
| Symptom | Inspect | Next step |
|---|---|---|
| Event wait times out | Event name, source object, trigger, predicate, and wait timeout | Arm the correct wait before the trigger and inspect predicate values. |
| Error says page or context closed | Lifecycle before event emission | Keep it alive or correct the flow that closes it. |
| Click or other action hangs | Dialog handler and action call log | Accept or dismiss registered dialogs; otherwise follow the actionability error. |
| Test reports a broader timeout | Test, assertion, action, navigation, fixture, or global scope | Identify the reported timeout class before changing settings. |
The Auto-waiting guide lists actionability checks. A call log often reveals whether Playwright was waiting for a locator, navigation, or event.
Debugging workflow you can apply to any event
- Reduce the case. Keep one page, one action, and one wait. Remove unrelated assertions and cleanup until the failure is isolated.
- Arm the wait immediately before the trigger. Create the promise on the exact page or context used by the action.
- Verify the trigger manually. Confirm that the control is unique, visible, enabled, and actually performs the behavior you named.
- Capture event data. For a popup, inspect its URL; for a download, inspect the suggested filename or path; for a context page, inspect which page object was created.
- Remove or loosen the predicate temporarily. Reintroduce it after you know the emitted data.
- Check dialogs and overlays. A registered but unresolved dialog can block the action; an overlay can fail actionability checks.
- Read the first failure. A click timeout followed by a test timeout is not evidence that the event itself was late.
- Review cleanup. Ensure fixtures do not close the page or context before the promise resolves.
- Set a targeted timeout only after diagnosis. Keep the value local to the wait when the delay is legitimate.
Correct patterns for common cases
Popup opened by a page
const popupPromise = page.waitForEvent('popup');
await page.getByRole('link', { name: 'Open details' }).click();
const popup = await popupPromise;
await popup.waitForLoadState('domcontentloaded');
await expect(popup).toHaveTitle(/Details/);
Download initiated by a click
const downloadPromise = page.waitForEvent('download');
await page.getByRole('button', { name: 'Download CSV' }).click();
const download = await downloadPromise;
await download.saveAs('artifacts/report.csv');
Any new page in a context
const newPagePromise = context.waitForEvent('page');
await page.getByRole('button', { name: 'Open in new tab' }).click();
const newPage = await newPagePromise;
await newPage.waitForLoadState('domcontentloaded');
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
If your actual goal is to obtain a clean screenshot rather than test a popup, download, or other Playwright event, ScreenshotNeo provides a single HTTP request. It is separate from fixing an event wait: use Playwright when you need browser interaction and assertions; use the screenshot API when you need an image or PDF of a URL.
ScreenshotNeo accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers report the page verdict and whether the request was billed. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo documentation for all request options and response details. The same request in Python is:
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)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
It also supports full-page captures with lazy images loaded, CSS-selector element captures, dark mode, 12 device presets or custom viewports, retina scale, PDF paper sizes and page ranges, HTML/CSS input, custom JavaScript and CSS, pre-capture clicks, selector hiding, selector or network-idle waits, request and resource blocking, custom headers, cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, selectable cache TTLs, signed public-image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Parameter names used by other screenshot APIs also work to ease migration.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallEvery plan includes every feature: 1,000 screenshots per month free with no card; Starter is $5 for 3,000, Growth $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000. Yearly billing provides two months free. Create a free ScreenshotNeo account to use the 1,000 monthly screenshots without a card.
FAQ
Can I wait for an event after clicking?
No. Register the promise first so the listener is active before the click or other trigger.
Why does a popup wait time out even though a new tab appears?
You may be listening on the wrong page. Use that page’s popup event for a popup it opened, or context.waitForEvent('page') when any context page may be created.
Should I set the timeout to zero?
Only if your installed Playwright version and configuration explicitly define that behavior for the relevant API. First establish that the event and trigger are correct; a longer timeout cannot fix a missing event.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemsWhat if I need to observe the request that opens a popup?
Use browser-context routing or request events. The popup event represents the new page becoming available after its initial navigation begins, not the instant application code calls window.open.
Frequently Asked Questions
Can I wait for an event after clicking?
No. Register the promise first so the listener is active before the click or other trigger.
Why does a popup wait time out even though a new tab appears?
You may be listening on the wrong page. Use that page’s popup event for a popup it opened, or context.waitForEvent(‘page’) when any context page may be created.
Should I set the timeout to zero?
Only if your installed Playwright version and configuration explicitly define that behavior for the relevant API. First establish that the event and trigger are correct; a longer timeout cannot fix a missing event.
What if I need to observe the request that opens a popup?
Use browser-context routing or request events. The popup event represents the new page becoming available after its initial navigation begins, not the instant application code calls window.open.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

