{"record":{"id":"d6548a7d9e101052","repo":"microsoft/playwright","slug":"selector-cannot-start-with-entering-frame-select","errorCode":null,"errorMessage":"Selector cannot start with entering frame, select the iframe first","messagePattern":"Selector cannot start with entering frame, select the iframe first","errorType":"exception","errorClass":"InvalidSelectorError","httpStatus":null,"severity":"error","filePath":"packages/isomorphic/selectorParser.ts","lineNumber":119,"sourceCode":"  };\n  let pierce = !!pierceByDefault;\n  let pierceToken = false;\n  let chunkStartIndex = 0;\n  for (let i = 0; i < selector.parts.length; ++i) {\n    const part = selector.parts[i];\n    if (part.name === 'internal:control' && (part.body === 'pierce-frames' || part.body === 'no-pierce-frames')) {\n      // Piercing applies to the whole selector, so the token only makes sense as the very first one.\n      if (i !== 0)\n        throw new InvalidSelectorError(`\"${part.body}\" is only allowed as the first selector token, while parsing selector ${selectorText}`);\n      pierce = part.body === 'pierce-frames';\n      pierceToken = true;\n      chunkStartIndex = i + 1;\n      continue;\n    }\n    if (part.name === 'internal:control' && part.body === 'enter-frame') {\n      const lastPart = chunk.parts[chunk.parts.length - 1];\n      if (!lastPart || (lastPart.name === 'internal:control' && lastPart.body === 'enter-frame'))\n        throw new InvalidSelectorError('Selector cannot start with entering frame, select the iframe first');\n      if (pierce) {\n        chunk.parts.push(part);\n        continue;\n      }\n      chunks.push(chunk);\n      chunk = { parts: [] };\n      chunkStartIndex = i + 1;\n      continue;\n    }\n    if (selector.capture === i)\n      chunk.capture = i - chunkStartIndex;\n    chunk.parts.push(part);\n  }\n  if (!chunk.parts.length) {\n    if (pierceToken)\n      throw new InvalidSelectorError(`Selector cannot be empty when piercing frames, while parsing selector ${selectorText}`);\n    throw new InvalidSelectorError(`Selector cannot end with entering frame, while parsing selector ${selectorText}`);\n  }","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/microsoft/playwright/blob/c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6/packages/isomorphic/selectorParser.ts#L101-L137","documentation":"splitSelectorByFrame rejects an 'enter-frame' control token when it begins a chunk — i.e. there is no preceding selector that picks the iframe element. Two consecutive enter-frame tokens also trip this (the chunk's last part is itself enter-frame). You must select the iframe element before descending into it.","triggerScenarios":"Writing a selector that starts with frame descent, e.g. internal:control=enter-frame >> button (no iframe chosen first), or two enter-frame tokens back to back without an iframe selector between them.","commonSituations":"Authoring iframe selectors by hand and forgetting the iframe locator prefix; chaining frame() calls incorrectly; converting a frame locator chain to a raw selector string and dropping the iframe token.","solutions":["Precede enter-frame with a selector matching the iframe element: 'iframe >> internal:control=enter-frame >> button'.","Use the frameLocator API: page.frameLocator('iframe').locator('button') — Playwright handles enter-frame for you.","Never put two enter-frame tokens back-to-back; always select an iframe between them."],"exampleFix":"// before\nawait page.locator('internal:control=enter-frame >> button').click();\n\n// after\nawait page.frameLocator('iframe').locator('button').click();","handlingStrategy":"try-catch","validationCode":"function selectorEntersFrameSafely(sel: string): boolean {\n  const parts = sel.split('>>').map(p => p.trim());\n  if (/internal:control=enter-frame/.test(parts[0])) return false;\n  for (let i = 1; i < parts.length; i++)\n    if (/internal:control=enter-frame/.test(parts[i]) && /internal:control=enter-frame/.test(parts[i-1])) return false;\n  return true;\n}","typeGuard":null,"tryCatchPattern":"try { await page.locator(sel).click(); }\ncatch (e) { if (isInvalidSelectorError(e) && /select the iframe first/.test(e.message)) { sel = 'iframe >> ' + sel; } else throw e; }","preventionTips":["Always select the iframe element before enter-frame.","Use page.frameLocator('iframe').locator(...) instead of raw enter-frame tokens.","Never place two enter-frame tokens back-to-back."],"tags":["selector","frames","iframe","ordering","invalid-selector"],"backgroundTag":null,"analyzedSha":"c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6","analyzedAt":"2026-08-12T07:26:36.950Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}