grafana/k6 · error
waiting for states of element
Error message
waiting for states %v of element %q
What it means
waitForElementStates() (the engine behind el.waitFor({state:...}) and actionability waits) evaluated its injected script but the result was neither a string ('done'/'error:...' protocol) nor a bool. This is an internal contract violation between the injected JS and the Go side, not a wait timeout — timeouts surface as ErrTimedOut.
Solutions
- Upgrade k6 to the latest version to clear internal regressions
- Reproduce on a static page; if clean there, audit the app for scripts that patch built-ins
- Replace the state wait with a polling loop on isVisible()/isEnabled() as a workaround
- Report a k6 issue with selector, states, and k6 version
Example fix
// workaround polling
// before
await el.waitFor({ state: 'visible' });
// after
let ok = false;
for (let i = 0; i < 50 && !ok; i++) {
ok = await el.isVisible();
if (!ok) await page.waitForTimeout(200);
} Defensive patterns
Strategy: try-catch
Try / catch
try {
await el.waitFor({ state: 'visible' });
} catch (e) {
if (/waiting for states/.test(String(e))) {
for (let i = 0; i < 50 && !(await el.isVisible()); i++) {
await page.waitForTimeout(200);
}
} else throw e;
} Prevention
- Keep k6 version pinned and updated in lockstep
- Suspect page-side script interference when invariant errors appear only on one app
When it happens
Trigger: Waiting for any element state where the evaluation returns an unexpected type: page-side tampering with the injected script's environment, or a k6 internal regression; all normal states (visible, hidden, enabled, disabled, editable) produce bool or string results.
Common situations: Hostile pages overriding primitives; k6 version skew after upgrades; essentially unreproducible on well-behaved pages.
Related errors
- checking element is in viewport: unexpected type %T
- checking state of element
- unexpected type %T
- unexpected value of type %T
- already belongs to the same execution context
AI-assisted analysis of grafana/k6@3fcf5388d7 (2026-08-18).
Data as JSON: /api/errors/63985052a143c6a1.
Report an issue: GitHub.
Appendix: source
Thrown at internal/js/modules/k6/browser/common/element_handle.go:625
opts := evalOptions{
forceCallable: true,
returnByValue: true,
}
result, err := h.evalWithScript(apiCtx, opts, fn, states, timeout.Milliseconds())
if err != nil {
return false, errorFromDOMError(err)
}
switch v := result.(type) {
case string: // Either we're done or an error happened (returned as "error:..." from JS)
if v == resultDone {
return true, nil
}
return false, errorFromDOMError(v)
case bool:
return v, nil
}
return false, fmt.Errorf(
"waiting for states %v of element %q", states, reflect.TypeOf(result))
}
// stepIntoFrame steps into an iframe/frame. Due to CORS, we need to perform this
// step outside of the browser (chromium). It returns the frame that it has stepped
// into and the selector to use within that frame.
func (h *ElementHandle) stepIntoFrame(
apiCtx context.Context, parsedSelector *Selector, frameNavIndex int, opts *FrameWaitForSelectorOptions,
) (*Frame, string, error) {
// Split selector at frame navigation boundary
beforeFrame, afterFrame := h.splitSelectorAtFrame(parsedSelector, frameNavIndex)
// Find the iframe element using the "before frame" selector
iframeSelector := h.reconstructSelector(beforeFrame)
iframeHandle, err := h.waitForSelector(apiCtx, iframeSelector, opts)
if err != nil {
return nil, "", fmt.Errorf("finding iframe with selector %q: %w", iframeSelector, err)View on GitHub (pinned to 3fcf5388d7)