{"record":{"id":"b9f43a54570bbc87","repo":"grafana/k6","slug":"element-is-not-attached-to-the-dom","errorCode":null,"errorMessage":"element is not attached to the DOM","messagePattern":"element is not attached to the DOM","errorType":"exception","errorClass":"ErrElementNotAttachedToDOM","httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/element_handle.go","lineNumber":28,"sourceCode":"\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/chromedp/cdproto/cdp\"\n\t\"github.com/chromedp/cdproto/dom\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\n\t\"go.k6.io/k6/v2/internal/js/modules/k6/browser/common/js\"\n\t\"go.k6.io/k6/v2/internal/js/modules/k6/browser/k6ext\"\n)\n\n// Common error types for element visibility.\nvar (\n\t// ErrElementNotVisible is returned when an element is not visible for an operation.\n\tErrElementNotVisible = errors.New(\"element is not visible\")\n\t// ErrElementNotAttachedToDOM is returned when an element is not attached to the DOM.\n\tErrElementNotAttachedToDOM = errors.New(\"element is not attached to the DOM\")\n)\n\nconst (\n\tresultDone       = \"done\"\n\tresultNeedsInput = \"needsinput\"\n)\n\ntype (\n\telementHandleActionFunc        func(context.Context, *ElementHandle) (any, error)\n\telementHandlePointerActionFunc func(context.Context, *ElementHandle, *Position) (any, error)\n\tretryablePointerActionFunc     func(context.Context, *ScrollIntoViewOptions) (any, error)\n\n\t// evalFunc is a common interface for both evalWithScript and eval.\n\t// It helps abstracting these methods to aid with testing.\n\tevalFunc func(ctx context.Context, opts evalOptions, js string, args ...any) (any, error)\n)\n\n// ElementHandle represents a HTML element JS object inside an execution context.","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/element_handle.go#L10-L46","documentation":"Sentinel error ErrElementNotAttachedToDOM from the k6 browser (chromium) module. It means the ElementHandle's backing DOM node no longer exists in the document: the node was removed by script, replaced by a re-render, or its frame navigated away. Operations like boundingBox(), click(), or element-state waits return it instead of acting on a dead reference, and the mapping layer (element_handle_mapping.go:22, locator_mapping.go:42) treats it as a retryable action failure.","triggerScenarios":"Calling ElementHandle or Locator actions (click, fill, check, waitForElementState, boundingBox) on a handle whose node was already removed; CDP returns 'error:notconnected' or an empty box model (element_handle.go:63-73, 1885, 1892); the element is replaced by an SPA re-render between the query and the action; the frame holding the element navigates and destroys the execution context.","commonSituations":"React/Vue apps replacing keyed nodes on re-render; handles captured early and used after the page changed; interacting while navigation is still settling; iframes removed mid-action.","solutions":["Re-query the element (page.$ or page.waitForSelector) immediately before the action instead of reusing an old handle","Prefer the Locator API (page.locator(...).click()), which re-resolves the element on each retry","Call page.waitForLoadState('load') or waitForNavigation first so the DOM is stable before querying","Wrap the action in try-catch and on this error retry the whole query+action sequence once"],"exampleFix":"// before\nconst btn = await page.$('#submit');\nawait page.goto('https://example.com/next');\nawait btn.click(); // handle is stale\n\n// after\nawait page.goto('https://example.com/next');\nconst btn = await page.waitForSelector('#submit');\nawait btn.click();","handlingStrategy":"retry","validationCode":"// Check the node is still connected before acting on a stored handle\nconst attached = await handle.evaluate(el => el.isConnected);\nif (!attached) {\n  handle = await page.waitForSelector(selector, { state: 'visible' });\n}\nawait handle.click();","typeGuard":null,"tryCatchPattern":"try {\n  await handle.click();\n} catch (e) {\n  if (String(e.message).includes('not attached')) {\n    handle = await page.waitForSelector(selector); // re-query\n    await handle.click(); // one retry with the fresh handle\n  } else { throw e; }\n}","preventionTips":["Prefer locators (page.locator(...)) over stored ElementHandles — they re-resolve on each attempt","Never reuse handles across navigations; re-query after every goto/waitForNavigation","Query elements only after the page load state has settled"],"tags":["browser","element-handle","dom","race-condition"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}