{"record":{"id":"979f5ca587e6844e","repo":"grafana/k6","slug":"element-is-not-an-iframe","errorCode":null,"errorMessage":"element is not an iframe","messagePattern":"element is not an iframe","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/element_handle.go","lineNumber":862,"sourceCode":"\t\treturn fmt.Errorf(\"clicking on element: %w\", err)\n\t}\n\tapplySlowMo(h.ctx)\n\n\treturn nil\n}\n\n// ContentFrame returns the frame that contains this element.\nfunc (h *ElementHandle) ContentFrame() (*Frame, error) {\n\tvar (\n\t\tnode *cdp.Node\n\t\terr  error\n\t)\n\taction := dom.DescribeNode().WithObjectID(h.remoteObject.ObjectID)\n\tif node, err = action.Do(cdp.WithExecutor(h.ctx, h.session)); err != nil {\n\t\treturn nil, fmt.Errorf(\"getting remote node %q: %w\", h.remoteObject.ObjectID, err)\n\t}\n\tif node == nil || node.FrameID == \"\" {\n\t\treturn nil, fmt.Errorf(\"element is not an iframe\")\n\t}\n\n\tframe, ok := h.frame.manager.getFrameByID(node.FrameID)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"frame not found for id %s\", node.FrameID)\n\t}\n\n\treturn frame, nil\n}\n\n// Dblclick scrolls element into view and double clicks on the element.\nfunc (h *ElementHandle) Dblclick(opts *ElementHandleDblclickOptions) error {\n\tdblclick := func(_ context.Context, handle *ElementHandle, p *Position) (any, error) {\n\t\treturn nil, handle.dblclick(p, opts.ToMouseClickOptions())\n\t}\n\tdblclickAction := h.newPointerAction(dblclick, &opts.ElementHandleBasePointerOptions)\n\tif _, err := call(h.ctx, dblclickAction, opts.Timeout); err != nil {\n\t\treturn fmt.Errorf(\"double clicking on element: %w\", err)","sourceCodeStart":844,"sourceCodeEnd":880,"githubUrl":"https://github.com/grafana/k6/blob/3fcf5388d78cb382f0c0d42ec556a06bfd1b8dee/internal/js/modules/k6/browser/common/element_handle.go#L844-L880","documentation":"ContentFrame() got a successful DescribeNode response but the node has no FrameID — by CDP semantics only iframe/frame elements carry one. k6 reports this as a plain 'element is not an iframe' error: you asked for the content frame of an element that is not a frame container.","triggerScenarios":"Calling contentFrame() on a plain element (div, span, ...), or using a frame-chained selector ('iframe >> button') where the prefix before '>>' actually matches a non-iframe node that merely wraps the real iframe; also elements inside the iframe itself being mistaken for the iframe.","commonSituations":"Selector matches a wrapper div around the iframe instead of the iframe; template components rendering the iframe inside a custom element; copied selectors from DevTools pointing at a container; refactor replaced the iframe with a portal/div.","solutions":["Fix the selector to target the <iframe> element itself: iframe#app, iframe[title=\"...\"], or frame,iframe","Verify with a tagName check before stepping in: await el.evaluate((n) => n.tagName) === 'IFRAME'","If the app swapped iframes for another mechanism, use page.frame()/waitForFrame on the actual frame URL or name instead","For wrapper containers, chain through the iframe: 'div.widget >> iframe >> button'"],"exampleFix":"// before\nconst frame = await page.$('.widget-container').contentFrame();\n\n// after\nconst frame = await page.$('.widget-container iframe').contentFrame();","handlingStrategy":"type-guard","validationCode":"const el = await page.$('.widget iframe'); // selector must end at the <iframe>\nif ((await el.evaluate((n) => n.tagName)) !== 'IFRAME') {\n  throw new Error('selector did not resolve to an iframe element');\n}","typeGuard":"async function contentFrameOf(page, selector) {\n  const el = await page.$(selector);\n  if (!el) return null;\n  const isIframe = (await el.evaluate((n) => n.tagName)) === 'IFRAME';\n  return isIframe ? await el.contentFrame() : null;\n}","tryCatchPattern":"try {\n  const frame = await el.contentFrame();\n} catch (e) {\n  if (/element is not an iframe/.test(String(e))) {\n    // selector matched a wrapper; descend to the real iframe\n    const frame = await (await page.$('.widget iframe')).contentFrame();\n  } else throw e;\n}","preventionTips":["End frame-crossing selectors at the <iframe> element itself, not a wrapper","Verify tagName === 'IFRAME' before contentFrame() when selectors come from DevTools copies","When apps replace iframes with portals, switch to page.frame({ name/url }) lookups"],"tags":["browser","iframe","content-frame","wrong-element-type","selector"],"backgroundTag":"element-wrong-type","analyzedSha":"3fcf5388d78cb382f0c0d42ec556a06bfd1b8dee","analyzedAt":"2026-08-18T03:05:52.393Z","contentChangedAt":"2026-08-18T03:05:52.393Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}