grafana/k6 · error
navigating %s to history entry %d: %w
Error message
navigating %s to history entry %d: %w
What it means
GoBackForward polls the page URL after navigating to a history entry (a 50ms ticker) because back/forward-cache restorations do not re-fire lifecycle events. If the wait is aborted — timeout exceeded, page context canceled, or the page's own context done — the error is wrapped here; a DeadlineExceeded is first converted to a k6ext.UserFriendlyError carrying the configured timeout.
Source
Thrown at internal/js/modules/k6/browser/common/page.go:1659
targetURL := entries[targetIndex].URL
timeoutCtx, timeoutCancelFn := context.WithTimeout(p.ctx, opts.Timeout)
defer timeoutCancelFn()
navAction := page.NavigateToHistoryEntry(historyEntryID)
if err := navAction.Do(cdp.WithExecutor(p.ctx, p.session)); err != nil {
return nil, fmt.Errorf("navigating to history entry %d: %w", historyEntryID, err)
}
wrapTimeoutError := func(err error) error {
if errors.Is(err, context.DeadlineExceeded) {
err = &k6ext.UserFriendlyError{
Err: err,
Timeout: opts.Timeout,
}
}
p.logger.Debugf("Page:GoBackForward", "timeoutCtx done: %v", err)
return fmt.Errorf("navigating %s to history entry %d: %w", direction, historyEntryID, err)
}
// Poll for URL change, don't rely on lifecycle events since bfcache
// restorations don't re-fire them.
ticker := time.NewTicker(50 * time.Millisecond)
defer ticker.Stop()
for {
select {
case <-p.ctx.Done():
return nil, ContextErr(p.ctx)
case <-timeoutCtx.Done():
return nil, wrapTimeoutError(ContextErr(timeoutCtx))
case <-ticker.C:
mainFrame := p.frameManager.MainFrame()
if mainFrame == nil {
continue
}View on GitHub (pinned to 93accf6570)
Solutions
- Increase the timeout option: page.goBack({ timeout: 30000 })
- Verify there is actually a different URL to go back to (check history via the response of goBack, which is null when there is none)
- Avoid goBack when the previous entry has the same URL — the URL-change poll cannot distinguish it from failure
- Close or abort-check the page before navigating if teardown races it
Example fix
// before
const resp = await page.goBack({ timeout: 1000 }); // too short under load
// after
const resp = await page.goBack({ timeout: 30000 }); Defensive patterns
Strategy: retry
Validate before calling
const historyResp = await page.goBack({ timeout: 30000 });
if (historyResp === null) console.warn('no history entry to navigate to'); Try / catch
try {
const res = await page.goBack({ timeout: 30000 });
} catch (e) {
if (/navigating (back|forward) to history entry/.test(e.message) && /timed out|timeout/i.test(e.message)) {
// URL never changed: same-URL entry or slow page — verify flow instead of retrying blindly
} else throw e;
} Prevention
- Pass an explicit generous timeout under load
- Do not use goBack to 'reload' a same-URL history entry — the URL poll cannot detect success
- Confirm the back entry actually differs in URL before waiting on it
When it happens
Trigger: page.goBack({timeout: N}) or goForward() where the URL never changes (history entry has the same URL, bfcache restore keeps the URL, or a hash-only change), so the poll runs until the timeout. Also fires when the page closes or the script context is canceled mid-poll.
Common situations: Navigating back to a same-URL entry; short timeout option (the default may be too small for slow pages under load); calling goBack() with no history entry change expected; k6 iteration aborting during the wait.
Related errors
- waiting for navigation: %w
- navigating to history entry %d: %w
- missing required argument 'url'
- errorText
- browser.on promise rejected: %w
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/5b7dd38c3d2dfdd7.
Report an issue: GitHub.