grafana/k6 · error

waiting for element state: %w

Error message

waiting for element state: %w

What it means

Actionability stage of pointer actions (non-force mode): waitForElementState(['visible','stable','enabled']) failed after the initial scroll. The injected waitForElementStates call either timed out (element never satisfies all three states) or returned a DOM error (element detached, not an element). This is the classic 'element is not actionable' failure for click/hover/tap.

Source

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

	// 4. Enabled
	// 5. Receives events
	pointerFn := func(apiCtx context.Context, sopts *ScrollIntoViewOptions) (res any, err error) {
		// We need to scroll the element into view first, otherwise we can
		// end up in a situation where the element is not in the correct state
		// (visible and stable, but could be enabled).
		err = h.scrollRectIntoViewIfNeeded(apiCtx, nil)
		if err != nil {
			return nil, fmt.Errorf("scrolling element into view: %w", err)
		}

		// Check if we should run actionability checks
		if !opts.Force {
			// As mentioned above, if we didn't scroll first, we could end
			// up stuck waiting indefinitely for the element to be in the
			// correct state.
			states := []string{"visible", "stable", "enabled"}
			if _, err = h.waitForElementState(apiCtx, states, opts.Timeout); err != nil {
				return nil, fmt.Errorf("waiting for element state: %w", err)
			}
		}

		// Decide position where a mouse down should happen if needed by action
		p := opts.Position

		// Change scrolling action depending on the scrolling options
		if sopts == nil {
			var rect *dom.Rect
			if p != nil {
				rect = &dom.Rect{X: p.X, Y: p.Y}
			}
			err = h.scrollRectIntoViewIfNeeded(apiCtx, rect)
		} else {
			_, err = h.eval(
				apiCtx,
				evalOptions{forceCallable: true, returnByValue: false},
				js.ScrollIntoView,

View on GitHub (pinned to 93accf6570)

Solutions

  1. Increase the action timeout: el.click({ timeout: '30s' })
  2. If the checks themselves are the blocker (e.g. deliberate animation), use force: true to skip the state wait
  3. Disable animations in the test environment or wait for a stable app state before acting
  4. For disabled controls, wait for the enabling condition (el.waitForElementState('enabled')) instead of hammering click

Example fix

// before
page.$('#save').click(); // button disabled during autosave

// after
const save = page.waitForSelector('#save', { state: 'visible' });
save.waitForElementState('enabled', { timeout: '20s' });
save.click();
Defensive patterns

Strategy: try-catch

Validate before calling

const el = page.waitForSelector('#save', { state: 'visible' });
el.waitForElementState('stable', { timeout: '10s' });
el.waitForElementState('enabled', { timeout: '10s' });
el.click();

Try / catch

try {
  el.click({ timeout: '30s' });
} catch (e) {
  const msg = String(e);
  if (msg.includes('waiting for element state')) {
    if (msg.includes('enabled')) throw new Error('control stayed disabled: ' + msg);
    el.click({ force: true }); // deliberate skip of state checks
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Element continuously animating (CSS transitions, carousels) so 'stable' never holds; disabled buttons failing 'enabled'; element toggling visibility on hover/jitter; timeout too short for the app's transitions; element detached mid-wait.

Common situations: Buttons with loading spinners that keep re-layouting; sticky elements recalculating position; disabled submit buttons gated on network validation that fails; fast default timeouts against slow staging environments.

Related errors


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