Even with auto-waiting, tests can fail or flake when an actionability check never becomes true (element stays hidden, disabled, or covered), when the locator is ambiguous, or when the test races an animation or network call. Diagnosing means reading Playwright's precise error and the trace rather than adding blind sleeps.
When an action times out, Playwright's error states exactly which check failed — e.g. 'element is not visible', 'element is outside of the viewport', 'element intercepts pointer events' (something is on top of it), or 'locator resolved to 2 elements' (strictness violation). Each points to a real cause: a modal overlay, a sticky header covering the target, a lazy-loaded element, or a genuinely disabled control. The fix is almost never a longer sleep; it's addressing the condition — waiting for the overlay to disappear, scrolling into view (which click does automatically), disambiguating the locator, or waiting for the network response the UI depends on. The Trace Viewer's DOM snapshots before/after the failing step usually reveal the cause instantly.
A click on 'Submit' fails with 'element intercepts pointer events'. The trace shows a cookie-consent banner overlapping the button. The fix isn't a sleep — it's dismissing the banner (or asserting it's gone) before clicking, so the button is actually reachable.
// FLAKY: acting before the overlay clears
await page.getByRole('button', { name: 'Submit' }).click(); // 'intercepts pointer events'
// FIX: remove the blocker first, then act
await page.getByRole('button', { name: 'Accept cookies' }).click();
await expect(page.getByRole('dialog', { name: 'Cookies' })).toBeHidden();
await page.getByRole('button', { name: 'Submit' }).click();
// FIX for a data-dependent action: wait for the response it relies on
await Promise.all([
page.waitForResponse(r => r.url().includes('/api/validate') && r.ok()),
page.getByRole('button', { name: 'Continue' }).click(),
]);Scenario: A test flakes ~1 in 10 runs with 'Timeout exceeded ... element intercepts pointer events' on a button that's clearly visible in the screenshot. What's happening and how do you fix it properly?