Playwright "strict mode violation" - Locator Resolved to Multiple
Playwright runs locators in strict mode: an action or assertion targeting a locator that matches more than one element is an error, not a "pick the first." The selector is too broad for the page.
What this error means
A click/fill/expect fails with "strict mode violation: locator resolved to 2 elements." The page legitimately has several matching nodes, and Playwright refuses to guess which one you meant.
Error: strict mode violation: getByRole('link', { name: 'Edit' })
resolved to 3 elements:
1) <a href="/items/1">Edit</a>
2) <a href="/items/2">Edit</a>
3) <a href="/items/3">Edit</a>Common causes
Locator matches repeated UI
A list or table renders the same label/role many times. A locator like getByText('Edit') matches every row, violating strict mode.
Name match is too loose
A substring name match (getByRole('button', { name: 'Save' })) also matches "Save and close," so more than one element qualifies.
How to fix it
Scope the locator to a unique ancestor
Chain from a row/region so the match is unique, or use an exact name.
// scope to the right row
await page.getByRole('row', { name: 'Invoice 42' })
.getByRole('link', { name: 'Edit' }).click();
// or require an exact name
await page.getByRole('button', { name: 'Save', exact: true }).click();Select a specific match when duplicates are expected
- Use
.first(),.last(), or.nth(i)only when you truly want a positional element. - Prefer a stable
getByTestIdper item over positional indexing. - Add a unique accessible name or test id to the target in the app.
How to prevent it
- Give actionable elements unique accessible names or test ids.
- Scope locators to a row/region instead of the whole page.
- Use
exact: truewhen a name is a prefix of another.