microsoft/playwright · error · InvalidSelectorError
Invalid frame in aria-ref selector "${selector}"
Error message
Invalid frame in aria-ref selector "${selector}" What it means
During internal aria-ref resolution (used by recorder/locator snapshots), Playwright extracts a frame sequence id from an aria-ref like 'f<seq>e<n>'. If no live frame in the page matches that sequence id, it throws InvalidSelectorError indicating the ref points at a frame that no longer exists.
Source
Thrown at packages/playwright-core/src/server/frameSelectors.ts:119
return [];
// Note: adopting elements one by one may be slow. If we encounter the issue here,
// we might introduce 'useMainContext' option or similar to speed things up.
const targetContext = await elementHandles[0]._frame.mainContext();
return Promise.all(elementHandles.map(handle => adoptIfNeeded(handle, targetContext)));
}
private _jumpToAriaRefFrameIfNeeded(selector: string, info: SelectorInfo, frame: Frame): Frame {
if (info.parsed.parts[0].name !== 'aria-ref')
return frame;
const body = info.parsed.parts[0].body as string;
const match = body.match(/^f(\d+)e\d+$/);
if (!match)
return frame;
const frameSeq = +match[1];
const jumptToFrame = this.frame._page.frameManager.frames().find(frame => frame.seq === frameSeq);
if (!jumptToFrame)
throw new InvalidSelectorError(`Invalid frame in aria-ref selector "${selector}"`);
return jumptToFrame;
}
private async _resolveFramesForSelector(selector: string, options: types.StrictOptions & { noDefaultPierce?: boolean } = {}, scope?: ElementHandle): Promise<SelectorInFrame[]> {
const pierceByDefault = !!this.frame._page.browserContext._options.pierceFrames && !options.noDefaultPierce;
const { pierce, chunks } = splitSelectorByFrame(selector, pierceByDefault);
for (const chunk of chunks) {
visitAllSelectorParts(chunk, (part, nested) => {
if (nested && part.name === 'internal:control' && part.body === 'enter-frame') {
const locator = asLocator(this.frame._page.browserContext._browser.sdkLanguage(), selector);
throw new InvalidSelectorError(`Frame locators are not allowed inside composite locators, while querying "${locator}"`);
}
if (nested && pierce) {
const locator = asLocator(this.frame._page.browserContext._browser.sdkLanguage(), selector);
throw new InvalidSelectorError(`Composite locators are not supported with piercing frames, while querying "${locator}"`);
}
});
}View on GitHub (pinned to c8fc3bf8d3)
Solutions
- Re-capture the selector/locator against the current page state.
- Wait for the target iframe to attach (frameLocator + waitFor) before resolving.
- Prefer stable, semantic locators (getByRole) over recorded refs when possible.
- Avoid reusing aria-refs across page navigations or sessions.
Example fix
null
Defensive patterns
Strategy: validation
Validate before calling
// Ensure the frame exists before using a recorded locator const frames = page.frames(); if (!frames.some(f => f.url().includes(expectedHost))) await page.waitForURL(expectedHost);
Type guard
null
Try / catch
null
Prevention
- Treat aria-refs as ephemeral; do not hardcode them.
- Re-record after intentional DOM/iframe restructuring.
When it happens
Trigger: Replaying a captured aria-ref/locator snapshot against a page where the referenced iframe has been removed, navigated, or not yet attached. Typically produced by recorder/replay or by reusing a stale ref captured in a previous session.
Common situations: Locator recorded against one page state and replayed after the DOM/iframes changed; iframes added dynamically and not yet present; cross-frame recording in flaky tests; recorder artifacts from older builds.
Related errors
- "${part.body}" is only allowed as the first selector token,
- Selector cannot start with entering frame, select the iframe
- Selector cannot be empty when piercing frames, while parsing
- Selector cannot end with entering frame, while parsing selec
- Can not capture the selector before diving into the frame. O
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/d0c9dde322c6b8ae.
Report an issue: GitHub.