{"record":{"id":"a384878e96b10960","repo":"grafana/k6","slug":"waiting-for-selector-q-w","errorCode":null,"errorMessage":"waiting for selector %q: %w","messagePattern":"waiting for selector %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/element_handle.go","lineNumber":1586,"sourceCode":"\n\treturn nil\n}\n\n// WaitForElementState waits for the element to reach the given state.\nfunc (h *ElementHandle) WaitForElementState(state string, opts *ElementHandleWaitForElementStateOptions) error {\n\t_, err := h.waitForElementState(h.ctx, []string{state}, opts.Timeout)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"waiting for element state %q: %w\", state, err)\n\t}\n\n\treturn nil\n}\n\n// WaitForSelector waits for the selector to appear in the DOM.\nfunc (h *ElementHandle) WaitForSelector(selector string, opts *FrameWaitForSelectorOptions) (*ElementHandle, error) {\n\thandle, err := h.waitForSelector(h.ctx, selector, opts)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"waiting for selector %q: %w\", selector, err)\n\t}\n\n\treturn handle, nil\n}\n\n// evalWithScript evaluates the given js code in the scope of this ElementHandle and returns the result.\n// The js code can call helper functions from injected_script.js.\nfunc (h *ElementHandle) evalWithScript(\n\tctx context.Context,\n\topts evalOptions, js string, args ...any,\n) (any, error) {\n\tscript, err := h.execCtx.getInjectedScript(h.ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"getting injected script: %w\", err)\n\t}\n\treturn h.eval(ctx, opts, js, append([]any{script}, args...)...)\n}\n","sourceCodeStart":1568,"sourceCodeEnd":1604,"githubUrl":"https://github.com/grafana/k6/blob/3fcf5388d78cb382f0c0d42ec556a06bfd1b8dee/internal/js/modules/k6/browser/common/element_handle.go#L1568-L1604","documentation":"Thrown by the k6 browser module's ElementHandle.waitForSelector(). It wraps failure of the internal frame-scoped wait: no matching element appeared before the timeout, the selector was rejected during compilation, or the frame was navigated away/closed mid-wait. Note: if the wait succeeds but the element is not attached (e.g., state:'attached' semantics), a nil handle can be returned without error — this error is specifically the failure branch.","triggerScenarios":"await el.waitForSelector('.child', { timeout }) where the child never renders (wrong selector, typo, scoped inside an element that is the wrong ancestor); waiting inside an iframe-embedded element after the iframe navigates; invalid selector syntax failing querySelector compilation; timeout shorter than app render time.","commonSituations":"Selectors that work in devtools but not scoped to the queried element (waitForSelector searches within the element's subtree); SPAs that render conditionally after async data arrives; slow backends in load tests making default timeouts too tight.","solutions":["Verify the selector actually matches within this element's subtree — test in devtools with document.querySelector from the element, not from document.","Increase the timeout: el.waitForSelector('.child', { timeout: '60s' }).","Use state: 'attached' when visibility is not required, or state: 'hidden' when waiting for removal (removal waits also use this API).","If the containing frame navigates, re-acquire the frame and element first."],"exampleFix":"// before\nconst row = await page.$('.order-row');\nconst badge = await row.waitForSelector('.shipped', { timeout: '2s' }); // data arrives later\n\n// after\nconst row = await page.waitForSelector('.order-row');\nconst badge = await row.waitForSelector('.shipped', { state: 'visible', timeout: '30s' });","handlingStrategy":"validation","validationCode":"// verify the selector matches within this element's subtree before waiting\nconst matches = await el.$$eval('*', nodes => nodes.some(n => n.matches('.shipped')));\nif (!matches) { throw new Error('selector never matches inside this element'); }\nawait el.waitForSelector('.shipped', { timeout: '30s' });","typeGuard":null,"tryCatchPattern":"try {\n  child = await el.waitForSelector('.child', { state: 'visible', timeout: '30s' });\n} catch (e) {\n  if (String(e).includes('waiting for selector')) {\n    await page.screenshot({ path: 'selector-timeout.png' }); // inspect actual DOM\n  }\n  throw e;\n}","preventionTips":["Test scoped selectors in devtools from the element, not from document.","Use state:'attached' when existence is all you need; visibility waits cost more and fail more."],"tags":["k6","browser","element-handle","selector","timeout"],"backgroundTag":"selector-not-found","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"}