Playwright: strict mode violation: locator resolved to N elements
Last verified
Error: locator.click: Error: strict mode violation: getByText('Sign in') resolved to 2 elements:
1) <a href="/login">Sign in</a> aka getByRole('link', { name: 'Sign in' })
2) <button type="submit">Sign in</button> aka getByRole('button', { name: 'Sign in' })What it means
Locators in Playwright are strict: any action that targets a single element (click, fill, check, hover, textContent and so on) requires the locator to match exactly one element. When it matches two or more, Playwright refuses to guess and throws immediately rather than clicking the first match. The number in the message is how many elements matched at the moment of the action.
The error is helpful. Below the headline Playwright lists every match, and for each one prints an aka line with a more specific locator that would uniquely identify it. Copying the right aka suggestion is often the whole fix.
What is not wrong: the page and timing are fine; the element you want is present. The problem is purely that the locator is ambiguous. Methods that are meant to work on many elements, such as locator.count() and locator.all(), do not trigger strictness.
What it looks like
// Ambiguous: two elements say "Sign in"
// await page.getByText('Sign in').click();
// Unique
await page.getByRole('button', { name: 'Sign in', exact: true }).click();
// Scoped to one row of a table
await page.getByRole('row')
.filter({ hasText: 'Invoice 42' })
.getByRole('button', { name: 'Edit' })
.click();Why it happens
- A text or role locator matches the same label used in several places, such as a header link and a form button both reading Sign in.
- The page renders duplicate markup for desktop and mobile layouts, with one copy hidden.
- A list or table repeats identical buttons (Edit, Delete) on every row.
- A substring match: getByText('Save') also matches 'Save draft' unless exact matching is used.
How to fix it
- Read the numbered list in the error and copy the aka locator of the element you meant.
- Narrow by role and accessible name: getByRole('button', { name: 'Sign in', exact: true }).
- Scope the search to a container: page.getByRole('form').getByRole('button', { name: 'Sign in' }).
- For repeated rows, filter first: page.getByRole('row').filter({ hasText: 'Invoice 42' }).getByRole('button', { name: 'Edit' }).
- If duplicates differ only by visibility, add .filter({ visible: true }) or its shortcut .visible() so only the rendered copy matches.
- Use .first(), .last() or .nth(i) only when position is genuinely what you mean.
How to stop it recurring
Write locators the way a user would describe the element: its role, its name and the region it sits in. Chain from a container (a dialog, form, row or main) instead of searching the whole page. Treat .first() as a smell: it hides strictness errors that would otherwise tell you the page changed. Adding stable data-testid attributes to repeated components removes most ambiguity for pages you control.
Official documentation: playwright.dev/docs/locators#strictness. See all Playwright error messages.
Related errors
Definitions
- PlaywrightPlaywright is Microsoft's open-source browser automation framework that provides a single API to control Chrom
- Chrome DevTools Protocol (CDP)The Chrome DevTools Protocol (CDP) is the JSON-over-WebSocket interface Chrome exposes for inspecting and cont
- CSS SelectorA CSS selector is a pattern used to identify and target specific HTML elements on a web page, widely used in b
- Headless ChromeHeadless Chrome is Google Chrome running without a visible window. It renders pages and runs JavaScript like n
- IframeAn iframe (inline frame) is an HTML element that embeds a separate web page within a parent page, creating an
- Web ElementA web element is any individual component rendered on a web page — buttons, links, input fields, images, text
Quick answers
What does "strict mode violation: locator resolved to N elements" mean in Playwright?
Locators in Playwright are strict: any action that targets a single element (click, fill, check, hover, textContent and so on) requires the locator to match exactly one element. When it matches two or more, Playwright refuses to guess and throws immediately rather than clicking the first match. The number in the message is how many elements matched at the moment of the action. The error is helpful.
How do I fix "strict mode violation: locator resolved to N elements"?
1. Read the numbered list in the error and copy the aka locator of the element you meant. 2. Narrow by role and accessible name: getByRole('button', { name: 'Sign in', exact: true }). 3. Scope the search to a container: page.getByRole('form').getByRole('button', { name: 'Sign in' }). 4. For repeated rows, filter first: page.getByRole('row').filter({ hasText: 'Invoice 42' }).getByRole('button', { name: 'Edit' }). 5. If duplicates differ only by visibility, add .filter({ visible: true }) or its shortcut .visible() so only the rendered copy matches. 6. Use .first(), .last() or .nth(i) only when…
Run browser automation you can watch and take over
Autonoly runs each job in a real cloud Chromium browser driven by Playwright. Timeouts and detached elements still happen; the difference is that you can watch the run live, see the step that failed, and take control of the browser to finish it.
Also see Live browser control.