{"record":{"id":"5202101ccc05fedf","repo":"grafana/k6","slug":"waitfor-retry-threshold-reached","errorCode":null,"errorMessage":"waitFor retry threshold reached","messagePattern":"waitFor retry threshold reached","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/frame.go","lineNumber":542,"sourceCode":"\t\t\tf.log.Warnf(\n\t\t\t\t\"Frame:waitForSelector\",\n\t\t\t\t\"fid:%s furl:%q sel:%q disposing element handle: %v\",\n\t\t\t\tf.ID(), f.URL(), selector, err,\n\t\t\t)\n\t\t}\n\t}\n\n\treturn adopted, nil\n}\n\nfunc (f *Frame) waitFor(\n\tselector string, opts *FrameWaitForSelectorOptions, retryCount int,\n) (_ *ElementHandle, rerr error) {\n\tf.log.Debugf(\"Frame:waitFor\", \"fid:%s furl:%q sel:%q\", f.ID(), f.URL(), selector)\n\n\tretryCount--\n\tif retryCount < 0 {\n\t\treturn nil, errors.New(\"waitFor retry threshold reached\")\n\t}\n\n\tdocument, err := f.document()\n\tif err != nil {\n\t\tif strings.Contains(err.Error(), \"Cannot find context with specified id\") {\n\t\t\treturn f.waitFor(selector, opts, retryCount)\n\t\t}\n\t\treturn nil, err\n\t}\n\n\thandle, err := document.waitForSelector(f.ctx, selector, opts)\n\tif err != nil {\n\t\tif strings.Contains(err.Error(), \"Inspected target navigated or closed\") {\n\t\t\treturn f.waitFor(selector, opts, retryCount)\n\t\t}\n\t\tif strings.Contains(err.Error(), \"Cannot find context with specified id\") {\n\t\t\treturn f.waitFor(selector, opts, retryCount)\n\t\t}","sourceCodeStart":524,"sourceCodeEnd":560,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/frame.go#L524-L560","documentation":"Frame.waitForSelector retries the internal DOM query up to 20 times when navigation destroys execution contexts ('Cannot find context with specified id', 'Execution context was destroyed', 'Inspected target navigated or closed') — frame.go:488-565. Each retry decrements retryCount; once it drops below 0 the wait gives up with 'waitFor retry threshold reached' rather than looping forever.","triggerScenarios":"The frame navigates continuously (redirect chains, meta refresh, repeated SPA transitions) so every waitFor attempt lands on a destroyed context; heavy iframe mounting/unmounting; waiting for a selector on a page that keeps reloading (e.g. auth redirects).","commonSituations":"Login flows with multiple redirects; ad-driven or polling pages that reload; CI environments where slow pages keep navigating; waiting on a selector in a frame that is being replaced.","solutions":["Let navigation settle first: await page.waitForLoadState('load') (or waitForNavigation) before waitForSelector","Assert the target URL is not a redirect loop and point the wait at the final destination frame","Retry the waitForSelector call at the script level with a bounded loop","If the page legitimately reloads in a loop, wait on a stable parent frame or use page-level waits instead of frame-level ones"],"exampleFix":"// before\nconst frame = page.frames()[1];\nawait frame.waitForSelector('#token'); // fails while frame keeps navigating\n\n// after\nawait page.waitForLoadState('load');\nconst frame = page.frames().find(f => f.name() === 'app');\nawait frame.waitForSelector('#token', { timeout: '10s' });","handlingStrategy":"retry","validationCode":"// Stabilize the page before waiting on a frame selector\nawait page.waitForLoadState('load');\nawait frame.waitForSelector(sel, { timeout: '10s' });","typeGuard":null,"tryCatchPattern":"let handle;\nfor (let attempt = 0; attempt < 3; attempt++) {\n  try { handle = await frame.waitForSelector(sel, { timeout: '5s' }); break; }\n  catch (e) {\n    if (!String(e.message).includes('retry threshold reached')) throw e;\n    await page.waitForLoadState('load');\n  }\n}","preventionTips":["Wait for load state before frame-level waits on pages that redirect or refresh","Verify the target URL is the final destination, not a redirect chain","Use page-level waits when the frame tree itself is churning"],"tags":["browser","frame","wait","retry","race-condition"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}