In Playwright, what is a strict mode violation and when does a locator raise one?
answer
- One element, or the call refuses
- The count is the failure, not visibility
- Message names the locator and the total
- Candidates printed with generated unique locators
- Zero matches waits; many matches does not
basics
~20 sPlaywright locators are strict: any call that targets a single DOM element throws a strict mode violation when the locator matches more than one. The error names the locator, the match count, and the candidates it found.
solid answer
~40 sEvery Playwright locator is strict by default. When a call needs one target element -- clicking, filling, reading text, or most `expect(locator)` matchers -- Playwright resolves the locator and throws if more than one node matched. The message reads `strict mode violation: <locator> resolved to N elements:` and then lists the candidates, each an HTML preview plus a locator Playwright generated that would match only that node. Zero matches is a different situation: the call auto-waits for an element to appear and eventually times out. More than one match is not something waiting can fix, so the violation is raised as soon as the locator resolves. Visibility does not matter -- a hidden duplicate still counts, because strictness is about how many DOM nodes matched. Multi-element calls such as `locator.count()` are exempt.
code
typescript · 11 linesimport { test, expect } from '@playwright/test';
test('two Delete buttons make the click throw', async ({ page }) => {
await page.setContent(`
<div id="comment-14"><button class="delete">Delete</button></div>
<div id="composer"><button class="delete">Delete</button></div>
`);
const error = await page.locator('button.delete').click().catch(e => e);
expect(error.message).toContain('strict mode violation');
expect(error.message).toContain('resolved to 2 elements');
});go deeper
Recall the rule and the message shape: a locator used for an action must match exactly one element, and the error prints the count plus every candidate it found.
Explain why more than one match fails at once while zero matches auto-waits, and that the count includes nodes hidden by CSS.
Show that you read the candidate list straight out of CI output to name the duplicate, and treat a new violation as a page change worth understanding rather than noise to suppress.
Own the position that a loud ambiguity failure beats a silent first-match guess, and hold the team to fixing locators or pages instead of reflexively appending a positional opt-out.
## What strictness means for a locator A Playwright `Locator` is not a captured reference to an element. It is a stored description of how to find one, and Playwright re-runs that description against the live page every time you use it. **Strictness** is the rule that decides what happens when the description matches more than one node: any call that has to act on a single element refuses to guess and throws instead. The rule is deliberately about the *number* of matches, not about which match looks most plausible. Playwright cannot know whether the second match is a stale duplicate, a hidden template, or the element you actually meant, so it stops and hands the decision back to you. ## The error message, line by line On an issue tracker's issue detail page, a locator that matches a "Delete" control in the comment composer and an identical one on an older comment produces something like: ``` Error: strict mode violation: locator('button.delete') resolved to 2 elements: 1) <button class="btn delete">Delete</button> aka locator('#comment-14').locator('button.delete') 2) <button class="btn delete">Delete</button> aka locator('#composer').locator('button.delete') ``` Three pieces of information are in there: - **the locator**, printed back in your language's syntax as Playwright understood it; - **the count** -- `resolved to 2 elements` -- which is the whole reason the call failed; - **the candidates**, each an HTML preview of the matched node followed by `aka` and a locator Playwright generated that would match only that node. The list is truncated with an ellipsis when there are many matches. The `aka` suggestions are the useful part. They are Playwright's own attempt at a unique locator for each candidate, so they usually tell you at a glance which one you meant and where the other lives. ## Strict calls versus multi-element calls | Call, on a locator matching 3 elements | Result | |---|---| | `locator.click()` | strict mode violation | | `locator.fill('urgent')` | strict mode violation | | `locator.textContent()` | strict mode violation | | `expect(locator).toBeVisible()` | strict mode violation | | `locator.count()` | returns `3` | | `expect(locator).toHaveCount(3)` | passes | The split is not arbitrary. A call is strict exactly when its result would be ambiguous with more than one element: there is no honest single value for "the text of these three nodes". `count()` is *about* the set, so a set of three is a perfectly good answer rather than a failure. ## Many matches is not the same as none Both are failures, but they fail differently, and confusing the two wastes debugging time: 1. **Zero matches.** The call auto-waits. Playwright keeps re-resolving the locator until an element appears or the timeout expires, then reports a timeout with a call log. 2. **More than one match.** The call throws straight away. The extra match is a fact about the page as it stands, and re-running the same query cannot reduce it, so there is nothing to retry. A test that fails in milliseconds with a candidate list has a strictness problem. A test that hangs and then times out has a "not found" or actionability problem. Raising the timeout does nothing for the first kind. ## Hidden elements still count Strictness counts nodes matched in the DOM, not nodes a user can see. A `display: none` copy of the issue toolbar, a collapsed sidebar, or a print-only template will each push the count to two even though only one is on screen. This is the most common surprise, and it shows up as: - a modal or drawer that renders its own copy of the page's action buttons; - a mobile layout and a desktop layout both in the document, one hidden by CSS; - a component library that keeps a hidden measurement clone of a control. ## What the error is asking you to do The point of failing is that the choice becomes explicit rather than silent. Two directions are open. Make the locator describe exactly one element, or opt out of strictness deliberately with `first()`, `last()` or `nth(i)` and accept that you are asking for a positional match. Playwright's documentation recommends the first and calls the second an escape hatch, because a positional match keeps following whatever lands at that index as the page changes.
- Does an element hidden with CSS still count toward the match total?Yes. Strictness counts nodes that match in the DOM, regardless of visibility, so a `display: none` duplicate still makes the total two. Actionability checks run after resolution, so the call never gets far enough to skip the hidden copy.
- Why doesn't Playwright simply act on the first match, as older tools did?Acting on the first match is silently wrong whenever the page grows a duplicate or changes order. Strictness turns that class of bug into a loud failure at the moment the ambiguity appears, with every candidate printed so the locator can be fixed deliberately.
It is like a warehouse pick list that names two bins for one part number: the picker stops and asks rather than grabbing whichever bin is nearer.
saying these in an interview costs you the question
- Claiming Playwright clicks the first match when several elements match
- Thinking a hidden duplicate is left out of the match count
- Believing the violation is a timeout that longer waits will fix
- Assuming only clicks are strict and reading text is exempt
- Saying the error means the element was not found