grafana/k6 · error

waiting for %q: %w

Error message

waiting for %q: %w

What it means

Thrown by Locator.WaitFor() when the wrapped frame.waitFor call fails after up to 20 internal retry rounds. Strict mode is forced on, so the selector must resolve to exactly one element. The dominant cause is a timeout: the element never reaches the requested state (attached/visible/hidden) before opts.Timeout expires; multiple matches also fail with a strict-mode violation.

Source

Thrown at internal/js/modules/k6/browser/common/locator.go:653

	opts.Strict = true
	if err := l.frame.dispatchEvent(l.selector, typ, eventInit, opts); err != nil {
		return fmt.Errorf("dispatching locator event %q to %q: %w", typ, l.selector, err)
	}

	applySlowMo(l.ctx)

	return nil
}

// WaitFor waits for the element matching the locator's selector with strict mode on.
func (l *Locator) WaitFor(opts *FrameWaitForSelectorOptions) error {
	l.log.Debugf("Locator:WaitFor", "fid:%s furl:%q sel:%q opts:%+v", l.frame.ID(), l.frame.URL(), l.selector, opts)

	opts.Strict = true
	_, err := l.frame.waitFor(l.selector, opts, 20)
	if err != nil {
		return fmt.Errorf("waiting for %q: %w", l.selector, err)
	}

	return nil
}

// DefaultTimeout returns the default timeout for the locator.
// This is an internal API and should not be used by users.
func (l *Locator) DefaultTimeout() time.Duration {
	return l.frame.defaultTimeout()
}

// FrameLocator represent a way to find element(s) in an iframe.
type FrameLocator struct {
	selector string

	frame *Frame

	ctx context.Context

View on GitHub (pinned to 93accf6570)

Solutions

  1. Verify the selector manually (devtools) and make it match exactly one element
  2. Increase opts.timeout to cover slow CI/load-test environments
  3. Choose the right state: 'attached' for existence, 'visible' for rendered, 'hidden' for disappearance
  4. If the element only exists after an action, put waitFor after that action, not before

Example fix

// before
await page.locator('.results-row').waitFor(); // default timeout too short in CI

// after
await page.locator('.results-row').waitFor({ state: 'visible', timeout: '60s' });
Defensive patterns

Strategy: try-catch

Validate before calling

// fail fast with a clearer message than the default timeout wrap
const count = await page.locator(sel).count();
if (count > 1) throw new Error(`strict mode: ${sel} matches ${count} elements`);
await page.locator(sel).waitFor({ state: 'visible', timeout: '60s' });

Type guard

async function isSingleMatch(page, sel) {
  const n = await page.locator(sel).count();
  return n === 1;
}

Try / catch

try {
  await page.locator(sel).waitFor({ timeout: '60s' });
} catch (e) {
  if (/timeout/i.test(e.message)) {
    throw new Error(`element never appeared: ${sel} (page url: ${page.url()})`);
  }
  throw e;
}

Prevention

When it happens

Trigger: locator.waitFor({state, timeout}) where exactly one element never appears/visibilize before the timeout; selector matches multiple elements (strict mode violation); waiting for state:'hidden' on an element that never disappears.

Common situations: Waiting for late SPA content with the default 30s timeout on slow or overloaded environments; selectors that match multiple nodes; asserting disappearance (state:'hidden') of elements that persist; wrong selector after a re-render.

Related errors


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