grafana/k6 · error
waiting for selector %q: %w
Error message
waiting for selector %q: %w
What it means
Thrown by Frame.WaitForSelector when waitForSelectorRetry (retried up to maxRetry = 1, so essentially one attempt plus one retry) fails. The wrapped error is usually a timeout ('timeout ... exceeded' from the action loop), a strict-mode violation (multiple matches), or an invalid selector. This is the single most common k6 browser error: the selector never satisfied the wait state within the timeout.
Source
Thrown at internal/js/modules/k6/browser/common/frame.go:2197
e := &k6ext.UserFriendlyError{
Err: err,
Timeout: opts.Timeout,
}
if opts.URL != "" {
return fmt.Errorf("waiting for navigation to URL matching %q: %w", opts.URL, e)
}
return fmt.Errorf("waiting for navigation: %w", e)
}
return nil
}
}
// WaitForSelector waits for the given selector to match the waiting criteria.
func (f *Frame) WaitForSelector(selector string, popts *FrameWaitForSelectorOptions) (*ElementHandle, error) {
handle, err := f.waitForSelectorRetry(selector, popts, maxRetry)
if err != nil {
return nil, fmt.Errorf("waiting for selector %q: %w", selector, err)
}
return handle, nil
}
// WaitForTimeout waits the specified amount of milliseconds.
func (f *Frame) WaitForTimeout(timeout int64) {
to := time.Duration(timeout) * time.Millisecond
f.log.Debugf("Frame:WaitForTimeout", "fid:%s furl:%q timeout:%s", f.ID(), f.URL(), to)
defer f.log.Debugf("Frame:WaitForTimeout:return", "fid:%s furl:%q timeout:%s", f.ID(), f.URL(), to)
select {
case <-f.ctx.Done():
case <-time.After(to):
}
}
View on GitHub (pinned to 93accf6570)
Solutions
- Verify the selector in browser DevTools on the exact page state: document.querySelectorAll(sel).length should be >= 1
- Increase timeout: page.waitForSelector(sel, { timeout: 60000 })
- If multiple elements match, make the selector unique or pass strict: false
- Confirm you are querying the right frame — for iframes get the frame first and wait on it
Example fix
// before
page.waitForSelector('.order-row'); // 0 matches, times out at 30s
// after
page.waitForSelector('#orders .order-row:first-child', { timeout: 60_000, state: 'attached' }); Defensive patterns
Strategy: validation
Validate before calling
const n = await page.evaluate((s) => document.querySelectorAll(s).length, sel);
if (n === 0) throw new Error(`selector ${sel} matches nothing; fix before waiting`);
if (n > 1) throw new Error(`selector ${sel} matches ${n} nodes; strict mode will fail`); Type guard
function isSelectorTimeout(e) {
return e instanceof Error && /waiting for selector/.test(e.message);
} Try / catch
try {
await page.waitForSelector(sel, { timeout: 60_000 });
} catch (e) {
if (isSelectorTimeout(e)) { /* assert why: auth failure? route not reached? */ }
throw e;
} Prevention
- Prefer data-test attributes over generated CSS selectors
- Use unique selectors and explicit timeouts
- Confirm login/auth steps succeeded before waiting for gated content
When it happens
Trigger: page.waitForSelector(sel) where the element never attaches; selector matches nothing because the app rendered a different DOM; strict: true (default in modern k6) with multiple matches; selector syntax error; frame detached while waiting.
Common situations: Selectors recorded from a different environment/build of the app; waiting for elements behind authentication when the login step failed silently; CI timing — element appears later than the default 30s timeout; iframes requiring frame.waitForSelector instead of page-level waits.
Related errors
- unchecking %q: %w
- double clicking on %q: %w
- getting text content of %q: %w
- finding iframe with selector %q: %w
- waiting for selector %q: %w
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/cb70d16eba359dfd.
Report an issue: GitHub.