Playwright strict mode violation
Updated 2026-11-13 · 2 min read
A single-element action matched more than one node. Reproduced examples show how distinct accessible names and row scoping identify the intended target.
The reproduced message
Captured with the repository's Playwright 1.65.0 and its Chromium browser on a tiny local page. This is the first line; the remaining diagnostic lists both matching buttons.
locator.click: Error: strict mode violation: getByRole('button', { name: 'Save', exact: true }) resolved to 2 elements:
The locator is valid but ambiguous for a click. Waiting longer does not decide which Save button the test meant.
1. Two controls have the same accessible name
This reproduction creates the ambiguity. Run inside an async function with an open Playwright page; the final line deliberately fails.
await page.setContent('<button>Save</button><button>Save</button>');
await page.getByRole('button', { name: 'Save', exact: true }).click({ timeout: 500 });
If these represent different actions, give them meaningful names. This isolated fix was run successfully:
await page.setContent('<button>Save</button><button>Save draft</button>');
await page.getByRole('button', { name: 'Save', exact: true }).click();
exact: true helps distinguish Save from Save draft. It cannot distinguish two buttons both named Save.
2. Repeated rows each contain the same action
Scope by the row's identity. This complete local fixture and click were run successfully:
await page.setContent(`
<table>
<tr><td>Booking A</td><td><button>Cancel</button></td></tr>
<tr><td>Booking B</td><td><button>Cancel</button></td></tr>
</table>`);
await page.getByRole('row').filter({ hasText: 'Booking B' })
.getByRole('button', { name: 'Cancel', exact: true }).click();
In a real booking test, identify the booking created by that test. A broad text filter can itself match multiple rows; inspect the resulting count when diagnosing it.
3. Both old and new panels are present
During a transition, two versions of a form may briefly coexist. Scope to a stable active region or wait for the old panel to be removed according to the application's behaviour. Do not assume getByRole will always exclude every unwanted duplicate: inspect the accessibility state and actual matches.
Prevention and interview answer
Keep locator intent in page objects and preserve traces for duplicate-match failures. Avoid .first() as a blanket repair: a changed DOM order could cancel the wrong booking. “Strictness found an ambiguous action; I narrowed the locator using the item's identity or a distinct accessible name.”
For no matching node, see locator timeouts. For malformed Selenium syntax, see InvalidSelectorException.
Quick answers
Why does count work while click fails?
Counting supports multiple matches. An action such as click needs one target.
Is first() always wrong?
No, but it is appropriate only when order is part of the requirement. It can hide unintended duplicate controls.
Sources
Want a project you can show in interviews?
The portfolio project kit walks you through testing a deliberately buggy app with Selenium or Playwright and API tests, then explaining it. The app itself is free.
