grafana/k6 · error

waiting for element state %q: %w

Error message

waiting for element state %q: %w

What it means

Wrapped failure from the public ElementHandle.WaitForElementState (elementHandle.waitForElementState(state)). The inner waitForElementState runs the injected waitForElementStates script with [state]; it fails when the element never reaches the state within opts.Timeout (mapped to ErrTimedOut via 'timed out' detection), when the state string is not one the injected script knows, or when the element is detached/not an element.

Source

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

		return nil, handle.typ(apiCtx, text, KeyboardOptions{})
	}
	typeAction := h.newAction(
		[]string{}, typ, false, withRetry, opts.NoWaitAfter, opts.Timeout,
	)
	if _, err := call(h.ctx, typeAction, opts.Timeout); err != nil {
		return fmt.Errorf("typing text %q: %w", text, err)
	}

	applySlowMo(h.ctx)

	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(

View on GitHub (pinned to 93accf6570)

Solutions

  1. Increase opts.Timeout: el.waitForElementState('visible', { timeout: '60s' })
  2. Use a supported state: visible, hidden, stable, enabled, editable (attached/detached belong to waitForSelector)
  3. If the underlying app is stuck (backend error), fix or stub that dependency instead of lengthening waits
  4. For 'hidden' waits on elements that may already be gone, tolerate detachment by catching and checking the element still exists

Example fix

// before
el.waitForElementState('attached', { timeout: '5s' }); // invalid state for this API

// after
el.waitForElementState('visible', { timeout: '30s' });
Defensive patterns

Strategy: validation

Validate before calling

const VALID_STATES = ['visible', 'hidden', 'stable', 'enabled', 'editable'];
function assertValidState(state) {
  if (!VALID_STATES.includes(state)) {
    throw new Error(`invalid element state ${state}; expected one of ${VALID_STATES.join(', ')}`);
  }
}
assertValidState(state);
el.waitForElementState(state, { timeout: '30s' });

Type guard

const isElementState = (s) =>
  ['visible', 'hidden', 'stable', 'enabled', 'editable'].includes(s);

Try / catch

try {
  el.waitForElementState(state, { timeout: '30s' });
} catch (e) {
  if (String(e).includes('timed out')) {
    // app never reached the state: fail with context, not a retry loop
    throw new Error(`element never became ${state}: ${e}`);
  }
  throw e;
}

Prevention

When it happens

Trigger: Waiting for 'visible' on an element that stays hidden until timeout; waiting for 'stable' on an infinitely animating element; passing an unsupported state string; waiting on a handle whose node was removed from the DOM.

Common situations: Spinners and skeletons that never resolve on failed backend calls; elements toggled by CSS transitions; waiting for 'enabled' on buttons gated by async form validation; states confused with waitForSelector states like 'attached'.

Related errors


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