grafana/k6 · error

finding iframe with selector %q: %w

Error message

finding iframe with selector %q: %w

What it means

stepIntoFrame() resolves the iframe part of a frame-crossing selector (e.g. 'iframe >> button') by waiting for the iframe element; this error wraps that wait failing — almost always a timeout waiting for the iframe element to attach, or a strict-mode violation when several elements match.

Source

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

	return false, fmt.Errorf(
		"waiting for states %v of element %q", states, reflect.TypeOf(result))
}

// stepIntoFrame steps into an iframe/frame. Due to CORS, we need to perform this
// step outside of the browser (chromium). It returns the frame that it has stepped
// into and the selector to use within that frame.
func (h *ElementHandle) stepIntoFrame(
	apiCtx context.Context, parsedSelector *Selector, frameNavIndex int, opts *FrameWaitForSelectorOptions,
) (*Frame, string, error) {
	// Split selector at frame navigation boundary
	beforeFrame, afterFrame := h.splitSelectorAtFrame(parsedSelector, frameNavIndex)

	// Find the iframe element using the "before frame" selector
	iframeSelector := h.reconstructSelector(beforeFrame)

	iframeHandle, err := h.waitForSelector(apiCtx, iframeSelector, opts)
	if err != nil {
		return nil, "", fmt.Errorf("finding iframe with selector %q: %w", iframeSelector, err)
	}

	// This is a valid response from waitForSelector. It means that the element
	// was either hidden or detached.
	if iframeHandle == nil {
		return nil, "", ErrElementNotVisible
	}

	frame, err := iframeHandle.ContentFrame()
	if err != nil {
		return nil, "", fmt.Errorf("getting iframe frame: %w", err)
	}

	// Wait for selector in the iframe using the "after frame" selector
	afterFrameSelector := h.reconstructSelector(afterFrame)

	return frame, afterFrameSelector, nil
}

View on GitHub (pinned to 93accf6570)

Solutions

  1. Increase the timeout option for the wait
  2. Verify the iframe part of the selector matches exactly one element
  3. Wait for the iframe to exist/load before querying inside it
  4. Prefer page.frameLocator() which re-resolves the frame chain robustly

Example fix

// before
await handle.waitForSelector('iframe >> .btn', { timeout: 1000 }); // finding iframe with selector

// after
await handle.waitForSelector('iframe >> .btn', { timeout: 30000 });
// or use the frame API:
const fl = page.frameLocator('#myframe');
await fl.locator('.btn').click();
Defensive patterns

Strategy: retry

Validate before calling

// Verify the iframe exists (and is unique) before crossing into it
const frames = await page.$$('iframe[src*="example"]');
if (frames.length !== 1) throw new Error(`expected 1 iframe, got ${frames.length}`);

Try / catch

try {
  await handle.waitForSelector('iframe >> .btn', { timeout: 30000 });
} catch (e) {
  if (String(e).includes('finding iframe with selector')) {
    await sleep(1000); // let the iframe attach, then retry
    await handle.waitForSelector('iframe >> .btn', { timeout: 30000 });
  } else { throw e; }
}

Prevention

When it happens

Trigger: elementHandle.waitForSelector()/query with a frame-crossing selector where the iframe element does not appear within opts.Timeout, matches multiple elements (Strict is forced true here), or the wait itself errors.

Common situations: Slow-loading or dynamically injected iframes; selector matching a wrapper instead of the iframe; ad/analytics iframes appearing late; default timeout too short for heavy pages.

Related errors


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