grafana/k6 · error
checking element is visible
Error message
checking element is visible: %w
What it means
ElementHandle.isVisible() runs waitForElementState(['visible']) with a zero timeout and intentionally ignores ErrTimedOut so that ordinary 'not visible yet' situations return false. A wrapped error means the check itself could not run: the element handle is detached/stale, the execution context was destroyed by navigation, or the frame/session closed.
Solutions
- Use a live locator: await page.locator(sel).isVisible(), which re-resolves the element
- Re-acquire the handle immediately before the visibility check
- Wrap in try/catch and treat the error as 'element gone' if that is the semantic you want
- Do not keep element handles across navigations or long asynchronous flows
Example fix
// before
const el = await page.$('.spinner');
await doWork();
const visible = await el.isVisible(); // throws: spinner unmounted
// after
await doWork();
const visible = await page.locator('.spinner').isVisible(); Defensive patterns
Strategy: try-catch
Validate before calling
// Live visibility check that re-resolves the element
const visible = await page.locator('.spinner').isVisible(); Try / catch
let visible = false;
try {
visible = await el.isVisible();
} catch (e) {
if (/not attached|Execution context|Could not find object/i.test(String(e.message))) {
visible = (await page.$(sel)) !== null && await page.locator(sel).isVisible();
} else throw e;
} Prevention
- Prefer page.locator(sel).isVisible() over checks on cached handles
- Guard visibility checks on elements that components unmount (spinners, toasts)
- Treat a thrown isVisible error as 'element gone', distinct from false ('present but hidden')
When it happens
Trigger: The element node was replaced or removed by an SPA re-render between query and check; the page navigated away, invalidating the handle's context; the element lives in a closed iframe; the browser session terminated.
Common situations: Visibility guards on spinner/loading indicators that get unmounted; handles cached across page.goto(); checks racing logout, popup close, or iteration teardown.
Related errors
- checking element is checked
- checking element is disabled
- checking element is editable
- checking element is enabled
- checking element is hidden
AI-assisted analysis of grafana/k6@3fcf5388d7 (2026-08-18).
Data as JSON: /api/errors/eecbfa6713d65c09.
Report an issue: GitHub.
Appendix: source
Thrown at internal/js/modules/k6/browser/common/element_handle.go:1113
}
// IsHidden checks if the element is hidden.
func (h *ElementHandle) IsHidden() (bool, error) {
ok, err := h.isHidden(h.ctx)
// We don't care anout timeout errors here!
if err != nil && !errors.Is(err, ErrTimedOut) {
return false, fmt.Errorf("checking element is hidden: %w", err)
}
return ok, nil
}
// IsVisible checks if the element is visible.
func (h *ElementHandle) IsVisible() (bool, error) {
ok, err := h.isVisible(h.ctx)
// We don't care anout timeout errors here!
if err != nil && !errors.Is(err, ErrTimedOut) {
return false, fmt.Errorf("checking element is visible: %w", err)
}
return ok, nil
}
// OwnerFrame returns the frame containing this element.
func (h *ElementHandle) OwnerFrame() (_ *Frame, rerr error) {
fn := `
(node, injected) => {
return injected.getDocumentElement(node);
}
`
opts := evalOptions{
forceCallable: true,
returnByValue: false,
}
res, err := h.evalWithScript(h.ctx, opts, fn)
if err != nil {View on GitHub (pinned to 3fcf5388d7)