grafana/k6 · error

waiting for selector %q did not result in any nodes

Error message

waiting for selector %q did not result in any nodes

What it means

Frame.waitForSelector runs an internal wait (frame.go:535) that retries up to 20 times on navigation-class errors. If the wait finishes without an error but produced no node handle — handle == nil — k6 reports this error instead of returning null like Playwright. The nil-handle outcome occurs when the wait resolves on a state that legitimately has no node: waiting with state:'hidden'/'detached' semantics where the element is absent or hidden, or the element matched and then disappeared before the handle was returned.

Source

Thrown at internal/js/modules/k6/browser/common/frame.go:499

	return nil, err
}

// waitForSelector will wait for the given selector to reach a defined state in
// opts.
//
// It will auto retry on certain errors until the retryCount is below 0. The
// retry workaround is needed since the underlying DOM can change when the
// wait action is performed during a navigation.
func (f *Frame) waitForSelector(selector string, opts *FrameWaitForSelectorOptions) (*ElementHandle, error) {
	f.log.Debugf("Frame:waitForSelector", "fid:%s furl:%q sel:%q", f.ID(), f.URL(), selector)

	handle, err := f.waitFor(selector, opts, 20)
	if err != nil {
		return nil, err
	}
	if handle == nil {
		return nil, fmt.Errorf("waiting for selector %q did not result in any nodes", selector)
	}

	// We always return ElementHandles in the main execution context (aka "DOM world")
	f.executionContextMu.RLock()
	defer f.executionContextMu.RUnlock()

	uec := f.executionContexts[utilityWorld]

	// An element should belong to the current main world execution context, and
	// not to the utility world context, otherwise, we should adopt it to the
	// current world's execution context. This is only valid when the handle
	// is from the current frame and not part of a nested frame.
	adopted := handle
	if uec != nil && uec == handle.execCtx {
		wec := f.executionContexts[mainWorld]
		if wec == nil {
			return nil, fmt.Errorf("waiting for selector %q: execution context %q not found", selector, mainWorld)
		}

View on GitHub (pinned to 93accf6570)

Solutions

  1. For absence checks, use waitForFunction(() => !document.querySelector(sel)) instead of waitForSelector with state:'hidden'
  2. Double-check the selector string (typos, wrong frame scope)
  3. If the element is expected to exist, wait with state:'visible' and a timeout that covers rendering
  4. Retry the wait once if a re-render race is likely

Example fix

// before
await page.waitForSelector('#toast', { state: 'hidden' }); // errors: did not result in any nodes

// after
await page.waitForFunction(() => !document.querySelector('#toast'));
Defensive patterns

Strategy: validation

Validate before calling

const el = await page.$('#toast');
if (!el) {
  // already absent: no need to wait
} else {
  await page.waitForFunction(() => !document.querySelector('#toast'));
}

Try / catch

try {
  await page.waitForSelector('#x', { state: 'visible' });
} catch (e) {
  if (/did not result in any nodes/.test(e.message)) {
    // element absent: treat as hidden or re-check the selector
  } else throw e;
}

Prevention

When it happens

Trigger: page.waitForSelector(sel, { state: 'hidden' }) where the element is/becomes absent — the wait succeeds with a nil handle and this error fires; an element matched then removed by a re-render before adoption; waiting in a frame whose document keeps changing.

Common situations: Porting Playwright scripts that expect waitForSelector({state:'hidden'}) to resolve null; SPAs aggressively re-rendering matched nodes; selectors that only match text or attributes with no element node.

Related errors


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