{"record":{"id":"1296a238ba53b2f1","repo":"grafana/k6","slug":"waiting-for-selector-q-did-not-result-in-any-node","errorCode":null,"errorMessage":"waiting for selector %q did not result in any nodes","messagePattern":"waiting for selector %q did not result in any nodes","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/frame.go","lineNumber":499,"sourceCode":"\n\treturn nil, err\n}\n\n// waitForSelector will wait for the given selector to reach a defined state in\n// opts.\n//\n// It will auto retry on certain errors until the retryCount is below 0. The\n// retry workaround is needed since the underlying DOM can change when the\n// wait action is performed during a navigation.\nfunc (f *Frame) waitForSelector(selector string, opts *FrameWaitForSelectorOptions) (*ElementHandle, error) {\n\tf.log.Debugf(\"Frame:waitForSelector\", \"fid:%s furl:%q sel:%q\", f.ID(), f.URL(), selector)\n\n\thandle, err := f.waitFor(selector, opts, 20)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif handle == nil {\n\t\treturn nil, fmt.Errorf(\"waiting for selector %q did not result in any nodes\", selector)\n\t}\n\n\t// We always return ElementHandles in the main execution context (aka \"DOM world\")\n\tf.executionContextMu.RLock()\n\tdefer f.executionContextMu.RUnlock()\n\n\tuec := f.executionContexts[utilityWorld]\n\n\t// An element should belong to the current main world execution context, and\n\t// not to the utility world context, otherwise, we should adopt it to the\n\t// current world's execution context. This is only valid when the handle\n\t// is from the current frame and not part of a nested frame.\n\tadopted := handle\n\tif uec != nil && uec == handle.execCtx {\n\t\twec := f.executionContexts[mainWorld]\n\t\tif wec == nil {\n\t\t\treturn nil, fmt.Errorf(\"waiting for selector %q: execution context %q not found\", selector, mainWorld)\n\t\t}","sourceCodeStart":481,"sourceCodeEnd":517,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/frame.go#L481-L517","documentation":"Frame.waitForSelector runs an internal wait (frame.go:535) that retries up to 20 times on navigation-class errors. If the wait finishes without an error but produced no node handle — handle == nil — k6 reports this error instead of returning null like Playwright. The nil-handle outcome occurs when the wait resolves on a state that legitimately has no node: waiting with state:'hidden'/'detached' semantics where the element is absent or hidden, or the element matched and then disappeared before the handle was returned.","triggerScenarios":"page.waitForSelector(sel, { state: 'hidden' }) where the element is/becomes absent — the wait succeeds with a nil handle and this error fires; an element matched then removed by a re-render before adoption; waiting in a frame whose document keeps changing.","commonSituations":"Porting Playwright scripts that expect waitForSelector({state:'hidden'}) to resolve null; SPAs aggressively re-rendering matched nodes; selectors that only match text or attributes with no element node.","solutions":["For absence checks, use waitForFunction(() => !document.querySelector(sel)) instead of waitForSelector with state:'hidden'","Double-check the selector string (typos, wrong frame scope)","If the element is expected to exist, wait with state:'visible' and a timeout that covers rendering","Retry the wait once if a re-render race is likely"],"exampleFix":"// before\nawait page.waitForSelector('#toast', { state: 'hidden' }); // errors: did not result in any nodes\n\n// after\nawait page.waitForFunction(() => !document.querySelector('#toast'));","handlingStrategy":"validation","validationCode":"const el = await page.$('#toast');\nif (!el) {\n  // already absent: no need to wait\n} else {\n  await page.waitForFunction(() => !document.querySelector('#toast'));\n}","typeGuard":null,"tryCatchPattern":"try {\n  await page.waitForSelector('#x', { state: 'visible' });\n} catch (e) {\n  if (/did not result in any nodes/.test(e.message)) {\n    // element absent: treat as hidden or re-check the selector\n  } else throw e;\n}","preventionTips":["Use waitForFunction for absence checks instead of waitForSelector state:'hidden'","Wait for presence with state:'visible'","Retry waits around re-render-heavy UIs"],"tags":["browser","wait-for-selector","state","playwright-difference"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}