{"record":{"id":"5d790d0dbf59e322","repo":"grafana/k6","slug":"got-a-nil-page-frame-tree","errorCode":null,"errorMessage":"got a nil page frame tree","messagePattern":"got a nil page frame tree","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/frame_session.go","lineNumber":438,"sourceCode":"\t\t\"sid:%v tid:%v\", fs.session.ID(), fs.targetID)\n\n\taction := cdppage.Enable()\n\tif err := action.Do(cdp.WithExecutor(fs.ctx, fs.session)); err != nil {\n\t\treturn fmt.Errorf(\"enabling page domain: %w\", err)\n\t}\n\n\tvar frameTree *cdppage.FrameTree\n\tvar err error\n\n\t// Recursively enumerate all existing frames in page to create initial in-memory structures\n\t// used for access and manipulation from JS.\n\taction2 := cdppage.GetFrameTree()\n\tif frameTree, err = action2.Do(cdp.WithExecutor(fs.ctx, fs.session)); err != nil {\n\t\treturn fmt.Errorf(\"getting page frame tree: %w\", err)\n\t} else if frameTree == nil {\n\t\t// This can happen with very short scripts when we might not have enough\n\t\t// time to initialize properly.\n\t\treturn fmt.Errorf(\"got a nil page frame tree\")\n\t}\n\n\t// Any new frame may have a child frame, not just mainframes.\n\tfs.handleFrameTree(frameTree, fs.isMainFrame())\n\n\tif fs.isMainFrame() {\n\t\tfs.initRendererEvents()\n\t}\n\treturn nil\n}\n\nfunc (fs *FrameSession) initIsolatedWorld(name string) error {\n\tfs.logger.Debugf(\"NewFrameSession:initIsolatedWorld\",\n\t\t\"sid:%v tid:%v\", fs.session.ID(), fs.targetID)\n\n\taction := cdppage.SetLifecycleEventsEnabled(true)\n\tif err := action.Do(cdp.WithExecutor(fs.ctx, fs.session)); err != nil {\n\t\treturn fmt.Errorf(\"enabling page lifecycle events: %w\", err)","sourceCodeStart":420,"sourceCodeEnd":456,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/frame_session.go#L420-L456","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Give the page work to do before ending the iteration: wait for a selector or lifecycle event instead of exiting instantly","Move browser.close() after all awaited page operations complete","Retry once — the race is timing-dependent and usually does not reproduce"],"exampleFix":"// before\nconst page = browser.newPage();\npage.goto('https://example.com/');\n// iteration ends immediately, init races teardown\n\n// after\nconst page = browser.newPage();\npage.goto('https://example.com/', { waitUntil: 'load' });\npage.waitForSelector('h1');","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const page = browser.newPage();\n  page.goto(url);\n} catch (e) {\n  if (/nil page frame tree/i.test(String(e))) {\n    // page torn down too fast — retry and let the page settle before exiting\n  }\n}","preventionTips":["Always wait for something concrete (selector, lifecycle event) before ending an iteration","Call browser.close() only after all page operations complete"],"tags":["browser","frame-tree","race-condition","teardown","transient"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}