microsoft/playwright · error · Error

Unable to generate locator for ${selector}

Error message

Unable to generate locator for ${selector}

What it means

Thrown by Frame.resolveSelector() when the generateSelectorSimple() injected script returns a falsy value for the matched element itself (not its parent frames). This means Playwright's selector generation engine could not produce a valid CSS/XPath selector for the element, which can happen with elements that lack distinguishing attributes or are in unusual DOM positions.

Source

Thrown at packages/playwright-core/src/server/frames.ts:1320

  async focus(progress: Progress, selector: string, options: types.StrictOptions & { noAutoWaiting?: boolean }) {
    dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options, (progress, handle) => handle._focus(progress)));
  }

  async blur(progress: Progress, selector: string, options: types.StrictOptions & { noAutoWaiting?: boolean }) {
    dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options, (progress, handle) => handle._blur(progress)));
  }

  async resolveSelector(progress: Progress, selector: string, options: { mainWorld?: boolean } = {}): Promise<{ resolvedSelector: string }> {
    const element = await progress.race(this.selectors.query(selector, options));
    if (!element)
      throw new Error(`No element matching ${selector}`);

    const generated = await progress.race(element.evaluateInUtility(async ([injected, node]) => {
      return injected.generateSelectorSimple(node as unknown as Element);
    }, {}));
    if (!generated)
      throw new Error(`Unable to generate locator for ${selector}`);

    let frame: Frame | null = element._frame;
    const result = [generated];
    while (frame?.parentFrame()) {
      const frameElement = await frame.frameElement(progress);
      if (frameElement) {
        const generated = await progress.race(frameElement.evaluateInUtility(async ([injected, node]) => {
          return injected.generateSelectorSimple(node as unknown as Element);
        }, {}));
        frameElement.dispose();
        if (generated === 'error:notconnected' || !generated)
          throw new Error(`Unable to generate locator for ${selector}`);
        result.push(generated);
      }
      frame = frame.parentFrame();
    }
    const resolvedSelector = result.reverse().join(' >> internal:control=enter-frame >> ');
    return { resolvedSelector };

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Add a stable identifier (id, data-testid) to the element in the page's HTML so the generator can target it.
  2. Use a hand-written selector instead of relying on resolveSelector's auto-generation.
  3. If using MCP tools, specify the selector explicitly rather than relying on resolution.

Example fix

// before — element has no distinguishing attributes
<button>Submit</button>
// resolveSelector fails to generate a locator

// after — add a data-testid
<button data-testid="submit-btn">Submit</button>
Defensive patterns

Strategy: validation

Validate before calling

// Add data-testid to elements to ensure reliable selector generation
// In page HTML: <div data-testid="my-element">

Try / catch

try {
  await frame.resolveSelector(selector);
} catch (e) {
  if (e.message.startsWith('Unable to generate locator')) {
    // Provide a hand-crafted selector instead
    return { resolvedSelector: manualSelector };
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling resolveSelector on an element that the selector generator cannot describe. This occurs when the element is deeply nested in a generic DOM structure with no unique attributes, or when the element's structure is too dynamic for the generator to produce a stable selector.

Common situations: Element is a bare <div> or <span> with no id, class, or data attributes among many siblings. Shadow DOM or custom elements that confuse the generator. Element was detached between the query and the generateSelector call.

Related errors


AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12). Data as JSON: /api/errors/03c84755c907f112. Report an issue: GitHub.