grafana/k6 · error
waitFor retry threshold reached
Error message
waitFor retry threshold reached
What it means
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.
Source
Thrown at internal/js/modules/k6/browser/common/frame.go:542
f.log.Warnf(
"Frame:waitForSelector",
"fid:%s furl:%q sel:%q disposing element handle: %v",
f.ID(), f.URL(), selector, err,
)
}
}
return adopted, nil
}
func (f *Frame) waitFor(
selector string, opts *FrameWaitForSelectorOptions, retryCount int,
) (_ *ElementHandle, rerr error) {
f.log.Debugf("Frame:waitFor", "fid:%s furl:%q sel:%q", f.ID(), f.URL(), selector)
retryCount--
if retryCount < 0 {
return nil, errors.New("waitFor retry threshold reached")
}
document, err := f.document()
if err != nil {
if strings.Contains(err.Error(), "Cannot find context with specified id") {
return f.waitFor(selector, opts, retryCount)
}
return nil, err
}
handle, err := document.waitForSelector(f.ctx, selector, opts)
if err != nil {
if strings.Contains(err.Error(), "Inspected target navigated or closed") {
return f.waitFor(selector, opts, retryCount)
}
if strings.Contains(err.Error(), "Cannot find context with specified id") {
return f.waitFor(selector, opts, retryCount)
}View on GitHub (pinned to 93accf6570)
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
Example fix
// before
const frame = page.frames()[1];
await frame.waitForSelector('#token'); // fails while frame keeps navigating
// after
await page.waitForLoadState('load');
const frame = page.frames().find(f => f.name() === 'app');
await frame.waitForSelector('#token', { timeout: '10s' }); Defensive patterns
Strategy: retry
Validate before calling
// Stabilize the page before waiting on a frame selector
await page.waitForLoadState('load');
await frame.waitForSelector(sel, { timeout: '10s' }); Try / catch
let handle;
for (let attempt = 0; attempt < 3; attempt++) {
try { handle = await frame.waitForSelector(sel, { timeout: '5s' }); break; }
catch (e) {
if (!String(e.message).includes('retry threshold reached')) throw e;
await page.waitForLoadState('load');
}
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- getting document element: nil document
- parent frame has been detached
- frame has been detached 2
- no frame found for id %s
- getting new document handle: %w
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/5202101ccc05fedf.
Report an issue: GitHub.