microsoft/playwright · error · InvalidSelectorError
Frame locators are not allowed inside composite locators, wh
Error message
Frame locators are not allowed inside composite locators, while querying "${locator}" What it means
Frame locators (the internal:control enter-frame part, i.e. frameLocator(...).find(...)) cannot be nested inside a composite (chained) locator. visitAllSelectorParts flags any nested enter-frame part and throws InvalidSelectorError naming the offending locator.
Source
Thrown at packages/playwright-core/src/server/frameSelectors.ts:130
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}"`);
}
});
}
if (pierce) {
const parsed = chunks[0]; // Only one chunk is allowed with pierce, it may contain enter-frame parts.
if (parsed.parts.some((part, index) => part.name === 'nth' && index !== parsed.parts.length - 1)) {
const locator = asLocator(this.frame._page.browserContext._browser.sdkLanguage(), selector);
throw new InvalidSelectorError(`nth can only be the last locator when piercing frames, while querying "${locator}"`);
}
return await this._resolveFramePiercingSelector(parsed, options, scope);
}
const result = await this._resolveChainedSelector(selector, options, chunks, scope);View on GitHub (pinned to c8fc3bf8d3)
Solutions
- Restructure so the frameLocator is the outer chain and only descendant locators live inside it.
- Use the frameLocator(...).locator(...).locator(...) API rather than string concatenation.
- Split the operation into explicit frame resolution + a within-frame locator query.
Example fix
// before (conceptual): enter-frame nested inside a composite
page.locator('iframe >> [data-x] >> iframe >> button'); // invalid nesting
// after: chain frame locators via the API
page.frameLocator('iframe').locator('[data-x]').locator('button'); Defensive patterns
Strategy: validation
Validate before calling
// Reject nested frame-locator strings before sending them
function hasNestedFrameLocator(sel: string) {
return (sel.match(/>>(?:[^>]*>>){2,}/) || []).length > 0;
} Type guard
null
Try / catch
null
Prevention
- Use the Locator API (frameLocator/locator) instead of raw selector strings.
- Never concatenate iframe-traversal tokens into one composite string.
When it happens
Trigger: Composing a selector where an iframe-descending locator is used as an inner/non-final part of a larger chain, e.g. combining frameLocator(...).locator(...) as an intermediate segment in a multi-part locator string.
Common situations: Building complex selectors by concatenation; converting hand-written query strings that mix iframe traversal with other composite operators; migrations from older selector syntax.
Related errors
- Error while parsing selector `${selector}` - selector cannot
- Composite locators are not supported with piercing frames, w
- options.visibility is not supported, did you mean options.st
- options.waitFor is not supported, did you mean options.state
- state: expected one of (attached|detached|visible|hidden)
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/aaacd73a4e2fd56c.
Report an issue: GitHub.