Introduction: What Does a Playwright Click Intercepted Error Mean?
A playwright click intercepted error occurs when Playwright attempts to click an element, but another element is positioned over the target or the target is not currently in a clickable state.
For example, suppose your test wants to click:
await page.getByRole(‘button’, { name: ‘Submit’ }).click();
but a cookie banner is covering the button.
Playwright does not simply send a blind mouse click. It performs actionability checks before clicking. It verifies that the target is visible, stable, enabled, and able to receive the click.
This behavior makes Playwright tests more reliable, but it can expose real UI problems such as:
- Cookie banners
- Modal dialogs
- Loading overlays
- Sticky headers
- Animations
- Layout shifts
- Incorrect locators
- Hidden elements
- Disabled buttons
- Responsive layout problems
This playwright click intercepted error fix tutorial explains how to identify the real cause and fix it without hiding genuine application defects.
What Causes a Playwright Click to Be Intercepted?
Common causes include:
- An overlay covers the target.
- A cookie popup blocks the button.
- A modal is still open.
- An animation is moving the target.
- The locator identifies the wrong element.
- The element is hidden.
- The element is disabled.
- The page has shifted.
- The element is outside the viewport.
- CI renders the page differently from the local machine.
The correct playwright click intercepted error fix depends on the actual cause.
Click Intercepted vs Element Not Found vs Timeout
These errors are related but different.
| Error | Meaning | Typical Cause |
| Click intercepted | Another element receives/blocks the click | Overlay, modal, popup |
| Element not found | Locator cannot find the target | Wrong selector |
| Timeout | Required actionability condition never becomes true | Hidden, disabled, moving element |
| Strict mode violation | Locator matches multiple elements | Unstable locator |
For example:
await page.getByRole(‘button’, { name: ‘Buy Now’ }).click();
If no matching button exists, this is an element/locator problem.
If the button exists but a modal covers it, it is a click interception problem.
How Playwright Auto-Waiting Handles Clicks
Playwright automatically waits for several actionability conditions before performing a click.
Conceptually, the target should be:
- Visible
- Stable
- Enabled
- Able to receive pointer events
Example:
await page.getByRole(‘button’, {
name: ‘Submit’
}).click();
You normally do not need:
await page.waitForTimeout(3000);
before the click.
Playwright’s auto-waiting is designed to synchronize with the page automatically.
If the element remains blocked, however, Playwright correctly reports a failure instead of pretending the click succeeded.
Fixing Playwright Click Intercepted Error Caused by Overlays
Problem → Cause → Fix → Best Practice
Problem
await page.getByRole(‘button’, {
name: ‘Checkout’
}).click();
fails because a loading overlay covers the button.
Cause
The overlay is still visible:
<div class=”loading-overlay”></div>
Fix
Wait for the overlay to disappear:
import { test, expect } from ‘@playwright/test’;
test(‘checkout’, async ({ page }) => {
await page.goto(‘https://example.com/checkout’);
const overlay = page.locator(‘.loading-overlay’);
await expect(overlay).toBeHidden();
await page.getByRole(‘button’, {
name: ‘Checkout’
}).click();
});
Best Practice
Wait for the actual blocking condition instead of adding an arbitrary sleep.
Cookie Popup Intercepting a Click
Cookie banners are one of the most common sources of a Playwright overlay issue.
Problem
await page.getByRole(‘button’, {
name: ‘Sign In’
}).click();
The cookie popup covers the button.
Correct Solution
Handle the popup first:
const cookieBanner = page.getByTestId(‘cookie-banner’);
if (await cookieBanner.isVisible()) {
await cookieBanner.getByRole(‘button’, {
name: ‘Accept’
}).click();
}
await page.getByRole(‘button’, {
name: ‘Sign In’
}).click();
An even better approach is to make cookie handling part of your reusable setup if it appears across many tests.
Modal Covering the Target Element
Suppose a promotional modal appears:
+—————————–+
| Special Offer |
| |
| [Close] |
+—————————–+
[Checkout]
The checkout button exists but cannot receive the click.
Fix
const modal = page.getByRole(‘dialog’);
if (await modal.isVisible()) {
await modal.getByRole(‘button’, {
name: ‘Close’
}).click();
}
await page.getByRole(‘button’, {
name: ‘Checkout’
}).click();
Best Practice
Do not hide the modal with JavaScript just to make the test pass. Handle it the same way a real user would.
Fixing Incorrect or Unstable Playwright Locators
Sometimes the apparent click interception problem is actually a locator problem.
Avoid overly broad selectors:
await page.locator(‘button’).nth(3).click();
The third button may change when the UI changes.
Prefer:
await page.getByRole(‘button’, {
name: ‘Submit Order’
}).click();
Or:
await page.getByTestId(‘submit-order’).click();
Good locators make the Playwright element not clickable fix much easier because the test interacts with the intended element.
Handling Animations and Moving Elements
Problem → Cause → Fix → Best Practice
Problem
A button moves while the test tries to click it.
Cause
CSS animation or transition:
.button {
transition: transform 0.5s;
}
Fix
Wait for a meaningful stable state instead of sleeping:
await expect(
page.getByRole(‘button’, { name: ‘Submit’ })
).toBeVisible();
await page.getByRole(‘button’, {
name: ‘Submit’
}).click();
For test environments, you can also disable animations through a stylesheet where appropriate.
Avoid:
await page.waitForTimeout(1000);
because animation timing can vary across machines.
Best Practice
Make animations deterministic in test environments whenever possible.
Handling Hidden and Disabled Elements
A visible-looking element may still be disabled.
For example:
<button disabled>Submit</button>
Waiting for it to be enabled is more reliable:
const submit = page.getByRole(‘button’, {
name: ‘Submit’
});
await expect(submit).toBeEnabled();
await submit.click();
If the element is intentionally disabled until form validation completes, wait for the condition that enables it.
Using scrollIntoViewIfNeeded()
Playwright generally handles scrolling automatically when performing actions.
However, explicit scrolling can sometimes help with complex layouts:
const button = page.getByRole(‘button’, {
name: ‘Submit’
});
await button.scrollIntoViewIfNeeded();
await button.click();
Use this when viewport position is genuinely relevant.
Do not add it mechanically to every click.
When and How to Use force: true
await button.click({
force: true
});
This bypasses some actionability checks.
It can be useful when you intentionally know that the normal checks do not represent the application’s behavior.
However, it should not be the default solution for a click intercepted error.
Why?
Suppose a modal genuinely blocks a button.
Using:
await button.click({ force: true });
may make the automation pass while the real user still cannot click the button.
That hides a genuine UI defect.
Better approach
await expect(page.getByRole(‘dialog’)).toBeHidden();
await button.click();
Use force: true only when bypassing actionability is intentional and understood.
Replacing Arbitrary waitForTimeout() Calls
Weak approach
await page.waitForTimeout(3000);
await page.getByRole(‘button’, { name: ‘Save’ }).click();
Better approach
const saveButton = page.getByRole(‘button’, {
name: ‘Save’
});
await expect(saveButton).toBeEnabled();
await saveButton.click();
The second version waits for the application state instead of guessing how long the page needs.
Debugging Playwright Click Failures
Playwright Inspector
Run:
npx playwright test –debug
This allows you to step through the test and inspect the page.
Screenshots
Capture the page before the click:
await page.screenshot({
path: ‘before-click.png’,
fullPage: true
});
This is particularly useful when an invisible-looking overlay is actually covering the element.
Trace Viewer
Enable tracing:
npx playwright test –trace=on
Then inspect:
npx playwright show-trace trace.zip
Trace Viewer helps you inspect the sequence of actions, screenshots, DOM state, and timing around the failed click.
Debugging Logs
You can also inspect the target:
const button = page.getByRole(‘button’, {
name: ‘Submit’
});
console.log(‘Visible:’, await button.isVisible());
console.log(‘Enabled:’, await button.isEnabled());
This can quickly distinguish a hidden/disabled target from an overlay problem.
Fixing Playwright Click Intercepted Errors in CI/CD
A common scenario is:
The click works locally but fails in CI.
Possible causes include:
- Different viewport size
- Different browser version
- Slower CPU
- Different fonts
- Animation timing
- Responsive layout
- Cookie banners
- Different test data
- Network latency
- Third-party popups
Practical CI troubleshooting
Run the test with one worker:
npx playwright test –workers=1
Enable tracing:
npx playwright test –trace=on
Capture screenshots and inspect the failed page.
Also make the viewport deterministic:
import { defineConfig } from ‘@playwright/test’;
export default defineConfig({
use: {
viewport: {
width: 1280,
height: 720
}
}
});
If CI displays a mobile-style layout because of a different viewport, the element covering your target may not even exist locally.
Real-World Playwright Click Intercepted Examples
Example 1: Loading Overlay
Problem: Checkout click fails.
Cause: .loading-overlay covers the button.
Fix:
await expect(
page.locator(‘.loading-overlay’)
).toBeHidden();
await page.getByRole(‘button’, {
name: ‘Checkout’
}).click();
Best Practice: Synchronize with the overlay state.
Example 2: Cookie Popup
Problem: Login button cannot be clicked.
Cause: Cookie banner covers it.
Fix:
const cookies = page.getByTestId(‘cookie-banner’);
if (await cookies.isVisible()) {
await cookies.getByRole(‘button’, {
name: ‘Accept’
}).click();
}
await page.getByRole(‘button’, {
name: ‘Login’
}).click();
Best Practice: Treat popup handling as part of the test setup.
Example 3: Modal
Problem: Delete button is blocked.
Cause: Confirmation or promotional modal remains open.
Fix:
const dialog = page.getByRole(‘dialog’);
await expect(dialog).toBeVisible();
await dialog.getByRole(‘button’, {
name: ‘Close’
}).click();
await page.getByRole(‘button’, {
name: ‘Delete’
}).click();
Best Practice: Close or complete the modal workflow before interacting with the underlying page.
Common Mistakes and Solutions
| Mistake | Solution |
| Immediately using force: true | Find the blocking element |
| Using waitForTimeout() | Wait for a real condition |
| Using nth() everywhere | Use accessible, stable locators |
| Ignoring cookie banners | Handle them explicitly |
| Ignoring modals | Close or complete the modal |
| Assuming local and CI layouts match | Fix viewport and environment |
| Clicking hidden elements | Wait for visibility |
| Clicking disabled elements | Wait for enabled state |
| Ignoring animations | Make animation timing deterministic |
Playwright Click Reliability Best Practices
Use this checklist:
- Prefer getByRole(), getByLabel(), and getByTestId().
- Let Playwright auto-wait before clicking.
- Handle overlays explicitly.
- Wait for modals to disappear.
- Avoid arbitrary sleeps.
- Make test data deterministic.
- Keep viewport settings consistent.
- Investigate layout shifts.
- Use screenshots and traces for failures.
- Use force: true sparingly.
- Test the same browser/environment in CI where possible.
- Do not hide real UI defects with aggressive waits.
The best Playwright click intercepted error fix is the one that preserves realistic user behavior while making synchronization deterministic.
Playwright Interview Questions With Answers
Why is Playwright click intercepted?
Usually because another element is covering the target, such as a modal, cookie banner, loading overlay, sticky header, or animated element.
How do you fix Playwright click intercepted error?
Identify the blocking element, wait for it to disappear or interact with it appropriately, then perform the normal click.
Does Playwright automatically wait before clicking?
Yes. Playwright performs actionability checks and waits for relevant conditions before performing actions.
Should you use force: true to fix every click error?
No. force: true bypasses actionability checks and can hide genuine UI problems.
How do you fix a Playwright element not clickable error?
Check whether the element is visible, enabled, stable, correctly located, inside the expected viewport, and free from overlays.
Why does a click work locally but fail in CI?
Differences in viewport, browser, performance, animations, responsive layouts, network timing, or test data can cause the click target to become blocked.
FAQs
What causes a Playwright click intercepted error?
The most common causes are overlays, popups, modals, animations, sticky elements, layout shifts, hidden elements, disabled controls, and incorrect locators.
How do I fix Playwright click intercepted error?
Find the element blocking the target, wait for it to disappear, close the popup/modal, correct the locator, or wait for the target to become actionable.
How do I fix a Playwright element not clickable problem?
Check visibility, enabled state, stability, viewport position, locator accuracy, and whether another element is receiving pointer events.
Can force: true fix a Playwright click error?
It can bypass some actionability checks, but it should only be used intentionally. It is not a replacement for fixing a genuine overlay or UI defect.
Why does Playwright click timeout?
The target may remain hidden, disabled, unstable, or blocked by another element until the action timeout expires.
Is waitForTimeout() a good solution for click errors?
Usually no. A deterministic condition such as toBeVisible(), toBeEnabled(), or waiting for an overlay to disappear is more reliable.
