{"record":{"id":"880bdcd10dc07773","repo":"microsoft/playwright","slug":"unable-to-retrieve-content-because-the-page-is-nav","errorCode":null,"errorMessage":"Unable to retrieve content because the page is navigating and changing the content.","messagePattern":"Unable to retrieve content because the page is navigating and changing the content\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/playwright-core/src/server/frames.ts","lineNumber":962,"sourceCode":"  async content(progress: Progress): Promise<string> {\n    return progress.race(this._content());\n  }\n\n  private async _content(): Promise<string> {\n    try {\n      const context = await this.utilityContext();\n      return await context.evaluate(() => {\n        let retVal = '';\n        if (document.doctype)\n          retVal = new XMLSerializer().serializeToString(document.doctype);\n        if (document.documentElement)\n          retVal += document.documentElement.outerHTML;\n        return retVal;\n      });\n    } catch (e) {\n      if (this.isNonRetriableError(e))\n        throw e;\n      throw new Error(`Unable to retrieve content because the page is navigating and changing the content.`);\n    }\n  }\n\n  async setContent(progress: Progress, html: string, options: types.NavigateOptions): Promise<void> {\n    const tag = `--playwright--set--content--${createGuid()}--`;\n    await this.raceNavigationAction(progress, async () => {\n      const waitUntil = options.waitUntil === undefined ? 'load' : options.waitUntil;\n      progress.log(`setting frame content, waiting until \"${waitUntil}\"`);\n      const context = await progress.race(this.utilityContext());\n      const tagPromise = new ManualPromise<void>();\n      this._page.frameManager._consoleMessageTags.set(tag, () => {\n        // Clear lifecycle right after document.open() - see 'tag' below.\n        this._onClearLifecycle();\n        tagPromise.resolve();\n      });\n      progress.setAllowConcurrentOrNestedRaces(true);\n      const lifecyclePromise = progress.race(tagPromise).then(() => this.waitForLoadState(progress, waitUntil));\n      const contentPromise = progress.race(context.evaluate(({ html, tag }) => {","sourceCodeStart":944,"sourceCodeEnd":980,"githubUrl":"https://github.com/microsoft/playwright/blob/c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6/packages/playwright-core/src/server/frames.ts#L944-L980","documentation":"Thrown by Frame._content() when the call to read document.documentElement.outerHTML via utilityContext().evaluate() fails for any reason that is not classified as non-retriable. This typically happens when a navigation starts while the serialized HTML is being read, destroying the execution context. The error is a catch-all wrapper replacing the underlying protocol error with a user-friendly message.","triggerScenarios":"Calling page.content() or frame.content() during or immediately after triggering a navigation (page.goto, click that navigates, form submit, meta-refresh, client-side router). The evaluate() round-trip to serialize the DOM fails because the document it was reading is torn down by the ongoing navigation. Also occurs with SPA route changes that call document.open()/document.write() concurrently.","commonSituations":"Test clicks a link and immediately calls page.content() without awaiting navigation. SPA test calls content() right after clicking a tab that triggers a route change. Race between a download or file dialog and content retrieval. setRedirect or server-side redirect chains where the page navigates multiple times in quick succession.","solutions":["Await the navigation before reading content: await page.waitForLoadState('domcontentloaded'); then await page.content().","Use page.waitForFunction(() => document.readyState === 'complete') after the action that triggers navigation, then call content().","Wrap content() in a retry loop with a small delay, since transient navigation races are often self-correcting.","If you need the HTML of the pre-navigation page, call page.content() before triggering the navigation, not after."],"exampleFix":"// before\nawait page.click('#navigate-away');\nconst html = await page.content(); // throws [320]\n\n// after\nawait Promise.all([\n  page.waitForNavigation(),\n  page.click('#navigate-away'),\n]);\nconst html = await page.content();","handlingStrategy":"retry","validationCode":"// Ensure navigation has settled before reading content\nasync function safeContent(page) {\n  await page.waitForLoadState('domcontentloaded');\n  return page.content();\n}","typeGuard":null,"tryCatchPattern":"try {\n  const html = await page.content();\n} catch (e) {\n  if (e.message.includes('navigating and changing the content')) {\n    await page.waitForLoadState('domcontentloaded');\n    return page.content(); // retry once\n  }\n  throw e;\n}","preventionTips":["Always await navigation or waitForLoadState before calling page.content().","Use page.waitForFunction(() => document.readyState === 'complete') for SPA route changes.","Capture content before triggering navigation if you need the pre-navigation HTML."],"tags":["navigation","content","frame","race-condition","dom"],"backgroundTag":null,"analyzedSha":"c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6","analyzedAt":"2026-08-12T07:26:36.950Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}