grafana/k6 · error

got a nil page frame tree

Error message

got a nil page frame tree

What it means

If Page.getFrameTree succeeds but returns a nil FrameTree (frame_session.go:438), initFrameTree returns this non-wrapped sentinel error. The code comment documents the cause: very short scripts where the page is torn down before initialization completes, so chromium reports no tree. It is a race between page lifetime and session setup.

Source

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

		"sid:%v tid:%v", fs.session.ID(), fs.targetID)

	action := cdppage.Enable()
	if err := action.Do(cdp.WithExecutor(fs.ctx, fs.session)); err != nil {
		return fmt.Errorf("enabling page domain: %w", err)
	}

	var frameTree *cdppage.FrameTree
	var err error

	// Recursively enumerate all existing frames in page to create initial in-memory structures
	// used for access and manipulation from JS.
	action2 := cdppage.GetFrameTree()
	if frameTree, err = action2.Do(cdp.WithExecutor(fs.ctx, fs.session)); err != nil {
		return fmt.Errorf("getting page frame tree: %w", err)
	} else if frameTree == nil {
		// This can happen with very short scripts when we might not have enough
		// time to initialize properly.
		return fmt.Errorf("got a nil page frame tree")
	}

	// Any new frame may have a child frame, not just mainframes.
	fs.handleFrameTree(frameTree, fs.isMainFrame())

	if fs.isMainFrame() {
		fs.initRendererEvents()
	}
	return nil
}

func (fs *FrameSession) initIsolatedWorld(name string) error {
	fs.logger.Debugf("NewFrameSession:initIsolatedWorld",
		"sid:%v tid:%v", fs.session.ID(), fs.targetID)

	action := cdppage.SetLifecycleEventsEnabled(true)
	if err := action.Do(cdp.WithExecutor(fs.ctx, fs.session)); err != nil {
		return fmt.Errorf("enabling page lifecycle events: %w", err)

View on GitHub (pinned to 93accf6570)

Solutions

  1. Give the page work to do before ending the iteration: wait for a selector or lifecycle event instead of exiting instantly
  2. Move browser.close() after all awaited page operations complete
  3. Retry once — the race is timing-dependent and usually does not reproduce

Example fix

// before
const page = browser.newPage();
page.goto('https://example.com/');
// iteration ends immediately, init races teardown

// after
const page = browser.newPage();
page.goto('https://example.com/', { waitUntil: 'load' });
page.waitForSelector('h1');
Defensive patterns

Strategy: retry

Try / catch

try {
  const page = browser.newPage();
  page.goto(url);
} catch (e) {
  if (/nil page frame tree/i.test(String(e))) {
    // page torn down too fast — retry and let the page settle before exiting
  }
}

Prevention

When it happens

Trigger: Scripts that create a page and close it (or the test ends) almost immediately; goto to about:blank followed by instant exit; browser.close() racing page init; k6 iteration ending within milliseconds of page creation.

Common situations: Smoke tests with a single quick goto and immediate exit, timeouts set near zero, or teardown (browser.close) firing while a lazy page/iframe session is still initializing.

Related errors


AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15). Data as JSON: /api/errors/5d790d0dbf59e322. Report an issue: GitHub.