grafana/k6 · error · UserFriendlyError

can't fetch the page for unknown reason

Error message

can't fetch the page for unknown reason

What it means

Browser.newPageInContext treats 'no page AND no error' after Target.createTarget as an impossible outcome and converts it into a wrapped UserFriendlyError with this message (internal/js/modules/k6/browser/common/browser.go:564). It fires when the CDP call neither failed nor produced a usable page before the context finished — typically the target was destroyed immediately, the browser died, or a timeout raced page creation.

Solutions

  1. Retry the page creation once with a short backoff — this is frequently a transient race
  2. Raise the browser timeout: K6_BROWSER_TIMEOUT=60s (or the timeout browser option)
  3. Check the remote browser's health and logs (container restarts, OOM kills) and give it more memory/CPU
  4. Ensure nothing closes the browser/context concurrently with newPage()
  5. Upgrade k6 — page-creation and timeout paths have been hardened across releases

Example fix

// before
const page = await browser.newPage();

// after
async function newPageWithRetry(browser, tries = 2) {
  for (let i = 0; ; i++) {
    try { return await browser.newPage(); }
    catch (e) {
      if (i >= tries || !/unknown reason/.test(e.message)) throw e;
      await new Promise((r) => setTimeout(r, 500));
    }
  }
}
const page = await newPageWithRetry(browser);
Defensive patterns

Strategy: retry

Validate before calling

// cheap pre-flight for a remote browser before creating pages
import http from 'k6/http';
const checkBrowserUp = (endpoint) => http.get(`http://${endpoint}/json/version`).status === 200;

Try / catch

async function newPageWithRetry(browser, tries = 2, delayMs = 500) {
  for (let i = 0; ; i++) {
    try {
      return await browser.newPage();
    } catch (e) {
      if (i >= tries || !/unknown reason/.test(String(e?.message || e))) throw e;
      await new Promise((r) => setTimeout(r, delayMs));
    }
  }
}

Prevention

When it happens

Trigger: browser.newPage() racing browser/context close; a remote wsURL browser crashing or being OOM-killed mid-call; a low browser timeout (K6_BROWSER_TIMEOUT / timeout option) expiring exactly during page creation; Chromium killing the target on startup (bad flags, corrupt data dir).

Common situations: Containerized/remote Chrome under memory pressure restarting between calls; scenarios launching many pages concurrently; flaky CI where the browser container is still starting; leftover chrome processes locking the user-data dir.

Related errors


AI-assisted analysis of grafana/k6@3fcf5388d7 (2026-08-18). Data as JSON: /api/errors/ef7d68107ba04bd1. Report an issue: GitHub.

Appendix: source

Thrown at internal/js/modules/k6/browser/common/browser.go:559

	case <-waitForPage:
		b.logger.Debugf("Browser:newPageInContext:<-waitForPage", "tid:%v bctxid:%v", tid, id)
		b.pagesMu.RLock()
		page = b.pages[tid]
		b.pagesMu.RUnlock()
	case <-ctx.Done():
		b.logger.Debugf("Browser:newPageInContext:<-ctx.Done", "tid:%v bctxid:%v err:%v", tid, id, ContextErr(ctx))
	}

	if err = ContextErr(ctx); err != nil {
		err = &k6ext.UserFriendlyError{
			Err:     err,
			Timeout: b.browserOpts.Timeout,
		}
	}

	if err == nil && page == nil {
		err = &k6ext.UserFriendlyError{
			Err: errors.New("can't fetch the page for unknown reason"),
		}
	}

	return page, err
}

// Close shuts down the browser.
func (b *Browser) Close() {
	if !b.closing.CompareAndSwap(false, true) {
		b.logger.Warnf(
			"Browser:Close",
			"Please call browser.close only once, and do not use the browser after calling close.",
		)
		return
	}
	// This will help with some cleanup in the connection and event loop above in
	// initEvents().
	defer b.browserCancelFn(errors.New("browser closed"))

View on GitHub (pinned to 3fcf5388d7)