{"record":{"id":"eecbfa6713d65c09","repo":"grafana/k6","slug":"checking-element-is-visible-w","errorCode":null,"errorMessage":"checking element is visible: %w","messagePattern":"checking element is visible: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/element_handle.go","lineNumber":1113,"sourceCode":"}\n\n// IsHidden checks if the element is hidden.\nfunc (h *ElementHandle) IsHidden() (bool, error) {\n\tok, err := h.isHidden(h.ctx)\n\t// We don't care anout timeout errors here!\n\tif err != nil && !errors.Is(err, ErrTimedOut) {\n\t\treturn false, fmt.Errorf(\"checking element is hidden: %w\", err)\n\t}\n\n\treturn ok, nil\n}\n\n// IsVisible checks if the element is visible.\nfunc (h *ElementHandle) IsVisible() (bool, error) {\n\tok, err := h.isVisible(h.ctx)\n\t// We don't care anout timeout errors here!\n\tif err != nil && !errors.Is(err, ErrTimedOut) {\n\t\treturn false, fmt.Errorf(\"checking element is visible: %w\", err)\n\t}\n\n\treturn ok, nil\n}\n\n// OwnerFrame returns the frame containing this element.\nfunc (h *ElementHandle) OwnerFrame() (_ *Frame, rerr error) {\n\tfn := `\n\t\t(node, injected) => {\n\t\t\treturn injected.getDocumentElement(node);\n\t\t}\n\t`\n\topts := evalOptions{\n\t\tforceCallable: true,\n\t\treturnByValue: false,\n\t}\n\tres, err := h.evalWithScript(h.ctx, opts, fn)\n\tif err != nil {","sourceCodeStart":1095,"sourceCodeEnd":1131,"githubUrl":"https://github.com/grafana/k6/blob/3fcf5388d78cb382f0c0d42ec556a06bfd1b8dee/internal/js/modules/k6/browser/common/element_handle.go#L1095-L1131","documentation":"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.","triggerScenarios":"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.","commonSituations":"Visibility guards on spinner/loading indicators that get unmounted; handles cached across page.goto(); checks racing logout, popup close, or iteration teardown.","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"],"exampleFix":"// before\nconst el = await page.$('.spinner');\nawait doWork();\nconst visible = await el.isVisible(); // throws: spinner unmounted\n\n// after\nawait doWork();\nconst visible = await page.locator('.spinner').isVisible();","handlingStrategy":"try-catch","validationCode":"// Live visibility check that re-resolves the element\nconst visible = await page.locator('.spinner').isVisible();","typeGuard":null,"tryCatchPattern":"let visible = false;\ntry {\n  visible = await el.isVisible();\n} catch (e) {\n  if (/not attached|Execution context|Could not find object/i.test(String(e.message))) {\n    visible = (await page.$(sel)) !== null && await page.locator(sel).isVisible();\n  } else throw e;\n}","preventionTips":["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')"],"tags":["browser","element-state","visible","stale-element","k6"],"backgroundTag":"stale-element-reference","analyzedSha":"3fcf5388d78cb382f0c0d42ec556a06bfd1b8dee","analyzedAt":"2026-08-18T03:05:52.393Z","contentChangedAt":"2026-08-18T03:05:52.393Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}