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
- Wait for the element to be present (await page.locator(sel).waitFor()) before calling ariaSnapshotJSON with that selector.
- Verify the selector is correct against the current DOM (e.g. via page.locator(sel).count()).
- 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
- Wait for the element to be attached before snapshotting by selector.
- Omit the selector for whole-page snapshots to use the body/frameset retry fallback.
- Validate selectors after UI refactors.
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
- Conflicting exactness in internal:role selector: ${stringify
- Unsupported token "${unsupportedToken.toSource()}" while par
- Error while parsing css selector "${selector}". Did you mean
- Malformed selector: ${part.name}=${part.body}
- "${parts[0].name}" selector cannot be first
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/aaaafe3d7466c435.
Report an issue: GitHub.