grafana/k6 · error
checking state %q of element %q
Error message
checking state %q of element %q
What it means
checkElementState evaluates injected.checkElementState(node, state) and expects either a string 'error:*' or a bool. If the JS side returns anything else (null/undefined/number), k6 reports 'checking state <state> of element <Go type>' — an internal contract violation between k6 and its injected script, usually triggered when the element or frame became invalid during evaluation.
Source
Thrown at internal/js/modules/k6/browser/common/element_handle.go:205
return injected.checkElementState(node, state);
}
`
opts := evalOptions{
forceCallable: true,
returnByValue: true,
}
result, err := h.evalWithScript(h.ctx, opts, fn, state)
if err != nil {
return nil, err
}
switch v := result.(type) {
case string: // An error happened (returned as "error:..." from JS)
return nil, errorFromDOMError(v)
case bool:
return &v, nil
}
return nil, fmt.Errorf(
"checking state %q of element %q", state, reflect.TypeOf(result))
}
func (h *ElementHandle) click(p *Position, opts *MouseClickOptions) error {
return h.frame.page.Mouse.click(p.X, p.Y, opts)
}
// This will get the clickable point of the element relative to the page even
// when the element is in an iframe. In some cases this isn't the case and we
// need to translate the point to the page.
func (h *ElementHandle) clickablePoint() (*Position, error) {
r, err := h.BoundingBox()
if err != nil {
return nil, fmt.Errorf("finding clickable point: %w", err)
}
return &Position{X: r.X + r.Width/2, Y: r.Y + r.Height/2}, nil
}
View on GitHub (pinned to 93accf6570)
Solutions
- Retry the waiting/action once after re-acquiring the locator — detached-node evaluations are transient.
- Upgrade to the latest k6 release to rule out an injected-script/binary mismatch.
- Avoid initiating actions exactly when a navigation is expected (wait for URL/content stability first).
- If reproducible on a static page, file a k6 issue with the script and browser version.
Example fix
// before
await page.locator('#status').click(); // actionability wait hits unexpected JS result
// -> checking state "visible" of element "<nil>"
// after
const loc = page.locator('#status');
await loc.waitFor({ state: 'visible' });
try { await loc.click(); }
catch (e) {
if (!String(e).includes('checking state')) throw e;
await page.locator('#status').click(); // re-acquire and retry
} Defensive patterns
Strategy: retry
Validate before calling
// make the wait state explicit and re-acquire the locator, keeping handles short-lived
await page.locator(sel).waitFor({ state: 'visible', timeout: 10_000 });
await page.locator(sel).click(); Try / catch
try {
await page.locator(sel).click();
} catch (e) {
if (/checking state .* of element/.test(String(e))) {
// injected-script contract broke (frame/node churn) — re-acquire and retry once
await page.locator(sel).waitFor({ state: 'visible' });
await page.locator(sel).click();
} else {
throw e;
}
} Prevention
- Keep element handles/locators short-lived in re-rendering SPAs; re-query before acting.
- Stay on the latest k6 release so the injected script bundle and binary stay in sync.
- Separate navigation phases from interaction phases to avoid evaluations racing navigation.
When it happens
Trigger: Waiting for an element state (visible/hidden/enabled/disabled/stable — used by waitFor and actionability checks before click/fill) when the frame navigates or the node is detached mid-evaluation so the injected script returns undefined instead of a bool; or a k6 version whose injected script bundle is out of sync.
Common situations: Auto-waiting on elements in SPAs that re-render aggressively; navigations racing actionability checks; upgrading k6 with a stale cached browser profile; extremely rare on healthy pages.
Related errors
- waiting for element state %q: %w
- finding clickable point: %w
- waiting for states %v of element %q
- filling element: %w
- selecting text: %w
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/067b05630519f0ff.
Report an issue: GitHub.