microsoft/playwright · error · NonRecoverableDOMError

Selector "${selector}" does not match any element

Error message

Selector "${selector}" does not match any element

What it means

Thrown by ariaSnapshotJSONForFrame when an explicit selector is passed to ariaSnapshotJSON but callOnSelector resolves to no element. With no selector the function falls back to body/frameset and retries, but when the caller supplies a selector and it matches nothing, the failure is non-recoverable (NonRecoverableDOMError) and stops retrying.

Source

Thrown at packages/playwright-core/src/server/page.ts:1131

}

export async function ariaSnapshotJSONForFrame(progress: Progress, frame: frames.Frame, selector: string | undefined, options: { mode?: 'ai' | 'default', doNotRenderActive?: boolean, depth?: number, boxes?: boolean, strict?: boolean, noDefaultPierce?: boolean } = {}): Promise<AriaSnapshotJSON> {
  const snapshot = await frame.retryWithProgressAndTimeouts(progress, [1000, 2000, 4000, 8000], async (progress, continuePolling) => {
    try {
      // Note: the resolved frame might differ from the original |frame|.
      // See https://developer.mozilla.org/en-US/docs/Web/API/Document/body for body/frameset explanation.
      // Non-strict, because pages with nested framesets have multiple "frameset" elements.
      const resolved = await progress.race(frame.selectors.callOnSelector(selector || 'body,frameset', { strict: options.strict ?? !!selector, noDefaultPierce: !selector || options.noDefaultPierce }, ({ injected, elements }, ariaOptions) => {
        return injected.ariaSnapshotJSON(elements[0], ariaOptions);
      }, {
        mode: options.mode ?? 'default',
        doNotRenderActive: options.doNotRenderActive,
        depth: options.depth,
        boxes: options.boxes,
      }));
      if (!resolved) {
        if (selector)
          throw new NonRecoverableDOMError(`Selector "${selector}" does not match any element`);
        // Retry only for the main frame "body" being absent, so that `page.ariaSnapshotJSON()` does not fail.
        return continuePolling;
      }
      return { ...resolved.result, resolvedFrame: resolved.frame };
    } catch (e) {
      if (frame.isNonRetriableError(e))
        throw e;
      return continuePolling;
    }
  });

  // Only fetch child snapshots for iframes that were actually rendered (not filtered by depth).
  const renderedIframeRefs = snapshot.iframeRefs.filter(ref => ref in snapshot.iframeDepths);
  progress.setAllowConcurrentOrNestedRaces(true);
  const childSnapshotPromises = renderedIframeRefs.map(async ref => {
    const childDepth = options.depth ? options.depth - snapshot.iframeDepths[ref] - 1 : undefined;
    // Non-strict, because child frameset documents have multiple "frameset" elements.
    const frameRootSelector = `aria-ref=${ref} >> internal:control=enter-frame >> body,frameset`;

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Wait for the element to be present (await page.locator(sel).waitFor()) before calling ariaSnapshotJSON with that selector.
  2. Verify the selector is correct against the current DOM (e.g. via page.locator(sel).count()).
  3. If you want a whole-page snapshot, omit the selector so the body/frameset fallback with retries applies.

Example fix

// before
const snap = await page.ariaSnapshotJSON({ /* via locator */ }); // selector matches nothing
// after
const loc = page.locator('#dashboard');
await loc.waitFor();
const snap = await loc.ariaSnapshotJSON();
Defensive patterns

Strategy: validation

Validate before calling

const loc = page.locator(selector);
if (await loc.count() === 0)
  throw new Error(`selector matches nothing: ${selector}`);
await loc.ariaSnapshotJSON();

Try / catch

try {
  await locator.ariaSnapshotJSON();
} catch (e) {
  if (/does not match any element/.test(e.message)) {
    await locator.waitFor({ state: 'attached' });
    // retry once, or fall back to page-level snapshot
  } else throw e;
}

Prevention

When it happens

Trigger: Calling page.ariaSnapshotJSON() or locator.ariaSnapshotJSON() (with a selector) on an element that does not exist in the DOM; passing a selector before the element has rendered; a typo or stale selector after navigation.

Common situations: Capturing an aria snapshot for AI/assertions against a selector whose element is conditionally rendered; calling the snapshot too early before SPA hydration; selector regression after a UI refactor.

Related errors


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