{"record":{"id":"ee882a3df687243c","repo":"microsoft/playwright","slug":"conflicting-exactness-in-internal-role-selector","errorCode":null,"errorMessage":"Conflicting exactness in internal:role selector: ${stringifySelector({ parts: [part] })}","messagePattern":"Conflicting exactness in internal:role selector: (.+?)\\)\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/isomorphic/locatorGenerators.ts","lineNumber":167,"sourceCode":"      continue;\n    }\n    if (part.name === 'internal:chain') {\n      const inners = innerAsLocators(factory, (part.body as NestedSelectorBody).parsed, false, maxOutputSize);\n      tokens.push(inners.map(inner => factory.generateLocator(base, 'chain', inner)));\n      continue;\n    }\n    if (part.name === 'internal:label') {\n      const { exact, text } = detectExact(part.body as string);\n      tokens.push([factory.generateLocator(base, 'label', text, { exact })]);\n      continue;\n    }\n    if (part.name === 'internal:role') {\n      const attrSelector = parseAttributeSelector(part.body as string, true);\n      const options: LocatorOptions = { attrs: [] };\n      for (const attr of attrSelector.attributes) {\n        if (attr.name === 'name') {\n          if (options.exact !== undefined && options.exact !== attr.caseSensitive)\n            throw new Error(`Conflicting exactness in internal:role selector: ${stringifySelector({ parts: [part] })}`);\n          options.exact = attr.caseSensitive;\n          options.name = attr.value;\n        } else if (attr.name === 'description') {\n          if (options.exact !== undefined && options.exact !== attr.caseSensitive)\n            throw new Error(`Conflicting exactness in internal:role selector: ${stringifySelector({ parts: [part] })}`);\n          options.exact = attr.caseSensitive;\n          options.description = attr.value;\n        } else {\n          if (attr.name === 'level' && typeof attr.value === 'string')\n            attr.value = +attr.value;\n          options.attrs!.push({ name: attr.name === 'include-hidden' ? 'includeHidden' : attr.name, value: attr.value });\n        }\n      }\n      tokens.push([factory.generateLocator(base, 'role', attrSelector.name, options)]);\n      continue;\n    }\n    if (part.name === 'internal:testid') {\n      const attrSelector = parseAttributeSelector(part.body as string, true);","sourceCodeStart":149,"sourceCodeEnd":185,"githubUrl":"https://github.com/microsoft/playwright/blob/c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6/packages/isomorphic/locatorGenerators.ts#L149-L185","documentation":"Thrown while converting an internal:role selector into a locator: the role's 'name' attribute and another already-processed attribute disagree on case-sensitivity. In Playwright's serialized form, case-sensitivity encodes exactness (case-sensitive => exact). If 'name' sets exact=true but a previously parsed attribute (e.g. an earlier 'description') set exact=false (or vice versa), the conflict is rejected.","triggerScenarios":"A hand-constructed or serialized internal:role selector where name= uses a case-sensitive matcher (e.g. name=\"X\" with the 's' flag) but a prior description= (or repeated name=) used a case-insensitive one, producing contradictory exactness. Equivalent userland: calling getByRole with conflicting exact semantics across name/description.","commonSituations":"Manually editing serialized locators or round-tripping them; programmatically building internal:role strings; mixing exact and non-exact matchers on the same role locator; bugs in code that generates selector strings from option maps.","solutions":["Make name and description matchers agree on case-sensitivity (both exact or both non-exact) in the role locator.","Stop hand-writing internal:role strings — use getByRole(role, { name, exact, description }) so Playwright serializes consistent flags.","Re-generate the locator with codegen and use that output verbatim."],"exampleFix":"// before (inconsistent flags)\ninternal:role=button[name=\"Save\"s][description=\"save\"] // name exact, description not\n\n// after (consistent)\ninternal:role=button[name=\"Save\"s][description=\"save\"s]","handlingStrategy":"validation","validationCode":"function roleExactnessConsistent(opts: {name?:{exact?:boolean}; description?:{exact?:boolean}}): boolean {\n  const e = opts.name?.exact;\n  const d = opts.description?.exact;\n  return e === undefined || d === undefined || e === d;\n}","typeGuard":null,"tryCatchPattern":"try { await page.locator(roleSelector).click(); }\ncatch (e) { if (/Conflicting exactness/.test(e.message)) { /* align flags and retry */ } else throw e; }","preventionTips":["Build role locators via getByRole with a single exact option, not hand-written internal:role strings.","Keep name and description matchers consistent in case-sensitivity.","Regenerate locators with codegen rather than editing serialized forms."],"tags":["locator","role","selector","exactness","serialization"],"backgroundTag":null,"analyzedSha":"c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6","analyzedAt":"2026-08-12T07:26:36.950Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}