grafana/k6 · error

element is not an iframe

Error message

element is not an iframe

What it means

ContentFrame() returns this when DOM.describeNode yields a node with no FrameID: the described node is not an iframe element. Only iframe elements own a content frame, so asking a regular element for one is a usage error.

Source

Thrown at internal/js/modules/k6/browser/common/element_handle.go:862

		return fmt.Errorf("clicking on element: %w", err)
	}
	applySlowMo(h.ctx)

	return nil
}

// ContentFrame returns the frame that contains this element.
func (h *ElementHandle) ContentFrame() (*Frame, error) {
	var (
		node *cdp.Node
		err  error
	)
	action := dom.DescribeNode().WithObjectID(h.remoteObject.ObjectID)
	if node, err = action.Do(cdp.WithExecutor(h.ctx, h.session)); err != nil {
		return nil, fmt.Errorf("getting remote node %q: %w", h.remoteObject.ObjectID, err)
	}
	if node == nil || node.FrameID == "" {
		return nil, fmt.Errorf("element is not an iframe")
	}

	frame, ok := h.frame.manager.getFrameByID(node.FrameID)
	if !ok {
		return nil, fmt.Errorf("frame not found for id %s", node.FrameID)
	}

	return frame, nil
}

// Dblclick scrolls element into view and double clicks on the element.
func (h *ElementHandle) Dblclick(opts *ElementHandleDblclickOptions) error {
	dblclick := func(_ context.Context, handle *ElementHandle, p *Position) (any, error) {
		return nil, handle.dblclick(p, opts.ToMouseClickOptions())
	}
	dblclickAction := h.newPointerAction(dblclick, &opts.ElementHandleBasePointerOptions)
	if _, err := call(h.ctx, dblclickAction, opts.Timeout); err != nil {
		return fmt.Errorf("double clicking on element: %w", err)

View on GitHub (pinned to 93accf6570)

Solutions

  1. Verify the element's tag is IFRAME before calling contentFrame()
  2. Tighten the selector to target the <iframe> itself
  3. Use page.frameLocator(selector) which encodes the intent explicitly

Example fix

// before
const frame = await (await page.$('#player')).contentFrame(); // element is not an iframe

// after
const frame = await (await page.$('#player iframe')).contentFrame();
// or
const fl = page.frameLocator('#player iframe');
Defensive patterns

Strategy: type-guard

Validate before calling

const isIframe = await handle.evaluate((el) => el.tagName === 'IFRAME');
if (!isIframe) throw new Error('contentFrame requires an <iframe> element');

Type guard

async function isFrameElement(handle) {
  return handle.evaluate((el) => el instanceof HTMLIFrameElement);
}

Try / catch

try {
  const frame = await handle.contentFrame();
} catch (e) {
  if (String(e).includes('element is not an iframe')) {
    // selector matched the wrong node; re-target the <iframe> itself
  } else { throw e; }
}

Prevention

When it happens

Trigger: handle.contentFrame() on a div/button/any non-iframe element; also frame-crossing selectors whose 'before frame' part resolves to a non-iframe node (then surfaced via 'getting iframe frame').

Common situations: Selector matches a wrapper div instead of the iframe; assuming an embedded player/widget is an iframe when it is a div/video; typos in the iframe selector.

Related errors


AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15). Data as JSON: /api/errors/979f5ca587e6844e. Report an issue: GitHub.