grafana/k6 · error

waiting for selector %q: %w

Error message

waiting for selector %q: %w

What it means

Wrapped failure from ElementHandle.WaitForSelector (elementHandle.waitForSelector(selector)). The inner path parses the selector (NewSelector — invalid syntax fails immediately), steps into frames when the selector contains iframe boundaries (>>), then waits for the target state. It fails on invalid selector syntax, on timeout waiting for the requested state, on strict-mode violations, or when the frame chain cannot be resolved.

Source

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

	return nil
}

// WaitForElementState waits for the element to reach the given state.
func (h *ElementHandle) WaitForElementState(state string, opts *ElementHandleWaitForElementStateOptions) error {
	_, err := h.waitForElementState(h.ctx, []string{state}, opts.Timeout)
	if err != nil {
		return fmt.Errorf("waiting for element state %q: %w", state, err)
	}

	return nil
}

// WaitForSelector waits for the selector to appear in the DOM.
func (h *ElementHandle) WaitForSelector(selector string, opts *FrameWaitForSelectorOptions) (*ElementHandle, error) {
	handle, err := h.waitForSelector(h.ctx, selector, opts)
	if err != nil {
		return nil, fmt.Errorf("waiting for selector %q: %w", selector, err)
	}

	return handle, nil
}

// evalWithScript evaluates the given js code in the scope of this ElementHandle and returns the result.
// The js code can call helper functions from injected_script.js.
func (h *ElementHandle) evalWithScript(
	ctx context.Context,
	opts evalOptions, js string, args ...any,
) (any, error) {
	script, err := h.execCtx.getInjectedScript(h.ctx)
	if err != nil {
		return nil, fmt.Errorf("getting injected script: %w", err)
	}
	return h.eval(ctx, opts, js, append([]any{script}, args...)...)
}

View on GitHub (pinned to 93accf6570)

Solutions

  1. Verify the selector matches in browser devtools against the exact page state, and prefer stable data-* attributes
  2. Increase the timeout and pick the right state: el.waitForSelector(sel, { state: 'visible', timeout: '30s' })
  3. For multiple matches, scope the selector more tightly or pass strict: false deliberately
  4. For chained iframe selectors, confirm each frame segment matches an existing <iframe> element

Example fix

// before
const ok = page.$('#form').waitForSelector('div.row', { timeout: '1s' });

// after
const ok = page.$('#form').waitForSelector('div.row[data-qa=row]', { state: 'visible', timeout: '30s' });
Defensive patterns

Strategy: validation

Validate before calling

// fail fast on syntax and emptiness before waiting
const parsed = page.$(selector);
if (parsed === null && strictModeWanted) {
  // let waitForSelector drive the wait, but assert the selector is well-formed
}
page.waitForLoadState('domcontentloaded');

Try / catch

try {
  return el.waitForSelector(sel, { state: 'visible', timeout: '30s' });
} catch (e) {
  const msg = String(e);
  if (msg.includes('strict mode violation')) {
    return el.waitForSelector(sel, { strict: false, timeout: '30s' });
  }
  if (msg.includes('waiting for selector')) {
    throw new Error(`selector ${sel} never matched: ${e}`);
  }
  throw e;
}

Prevention

When it happens

Trigger: Malformed CSS/XPath/text selector that NewSelector rejects; selector never matching before opts.Timeout; state: 'visible' while the element exists but stays hidden; strict: true with multiple matches ('strict mode violation, multiple elements returned for selector query'); iframe part (>> iframe ...) of a chained selector not found, giving 'finding iframe with selector' failures.

Common situations: Selectors authored against one environment but the app renders differently in another; waiting inside an element handle scope where the sub-tree never gets the node; dynamic class names/IDs from CSS-in-JS; strict-by-default queries hitting duplicated DOM.

Related errors


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