{"record":{"id":"15eb956e02cfd718","repo":"grafana/k6","slug":"node-is-either-not-visible-or-not-an-htmlelement","errorCode":null,"errorMessage":"node is either not visible or not an HTMLElement: %w","messagePattern":"node is either not visible or not an HTMLElement: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/screenshotter.go","lineNumber":304,"sourceCode":"\nfunc (s *screenshotter) screenshotElement(\n\th *ElementHandle, opts *ElementHandleScreenshotOptions,\n) (buf []byte, err error) {\n\tformat := opts.Format\n\tviewportSize, err := s.originalViewportSize(h.frame.page)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"getting original viewport size: %w\", err)\n\t}\n\toriginalSize := h.frame.page.emulatedSize\n\n\terr = h.waitAndScrollIntoViewIfNeeded(h.ctx, false, true, opts.Timeout)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"scrolling element into view: %w\", err)\n\t}\n\n\tbbox, err := h.boundingBox()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"node is either not visible or not an HTMLElement: %w\", err)\n\t}\n\tif bbox.Width <= 0 {\n\t\treturn nil, fmt.Errorf(\"node has 0 width\")\n\t}\n\tif bbox.Height <= 0 {\n\t\treturn nil, fmt.Errorf(\"node has 0 height\")\n\t}\n\n\tfitsViewport := bbox.Width <= viewportSize.Width && bbox.Height <= viewportSize.Height\n\tif !fitsViewport { //nolint:nestif\n\t\toverriddenViewportSize := Size{\n\t\t\tWidth:  math.Max(viewportSize.Width, bbox.Width),\n\t\t\tHeight: math.Max(viewportSize.Height, bbox.Height),\n\t\t}.enclosingIntSize()\n\t\tdefer func() {\n\t\t\tif restoreErr := s.restoreViewport(h.frame.page, originalSize); restoreErr != nil {\n\t\t\t\terr = errors.Join(err, fmt.Errorf(\"restoring viewport: %w\", restoreErr))\n\t\t\t}","sourceCodeStart":286,"sourceCodeEnd":322,"githubUrl":"https://github.com/grafana/k6/blob/3fcf5388d78cb382f0c0d42ec556a06bfd1b8dee/internal/js/modules/k6/browser/common/screenshotter.go#L286-L322","documentation":"Element-screenshot step: after scrolling into view, boundingBox() failed, so the module reports the element is either not visible or not an HTMLElement. boundingBox needs the element's box model via CDP; failures mean the node has no box (not rendered), was detached, or is not an element node (e.g. a text node handle).","triggerScenarios":"Screenshotting a hidden element (display:none/visibility:hidden ancestor), an element detached between scroll and measurement, an SVG-in-shadow-DOM edge case, or a handle to a non-element node.","commonSituations":"SPA re-render detaching the node mid-call; elements inside collapsed accordions/tabs not opened; content in an iframe whose layout changed; screenshotting immediately after style changes with transitions pending.","solutions":["Ensure the element is actually visible and expanded (open the accordion/tab, remove display:none) before the screenshot.","Re-query the element right before capturing so it cannot be detached.","Wait for visible state and for animations/transitions to finish.","Check the wrapped error for detaches — 'node is detached from the document'."],"exampleFix":"// before\nawait page.click('#details-summary'); // panel animating open\nawait page.$('#details-body').screenshot({ path: 'd.png' });\n\n// after\nawait page.click('#details-summary');\nconst el = page.$('#details-body');\nawait el.waitFor({ state: 'visible' });\nawait el.screenshot({ path: 'd.png' });","handlingStrategy":"type-guard","validationCode":"await handle.waitFor({ state: 'visible' });\nconst box = await handle.boundingBox();\nif (!box || box.width <= 0 || box.height <= 0) throw new Error('element not renderable');\nawait handle.screenshot({ path: 'e.png' });","typeGuard":"const isRenderableElement = async (h) => {\n  const b = await h.boundingBox();\n  return !!b && b.width > 0 && b.height > 0;\n};","tryCatchPattern":"try {\n  await handle.screenshot({ path: 'e.png' });\n} catch (e) {\n  if (/not visible or not an HTMLElement/i.test(String(e))) {\n    // element hidden/detached: open container or re-query before retrying\n    await page.locator(sel).waitFor({ state: 'visible' });\n    await page.locator(sel).screenshot({ path: 'e.png' });\n  } else throw e;\n}","preventionTips":["Open collapsed containers (accordions, tabs) before capturing their content.","Re-locate elements right before screenshots in SPAs that re-render.","Wait for animations to complete so the box model is final."],"tags":["k6","browser","screenshot","element-handle","visibility"],"backgroundTag":"element-not-actionable","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"}