diff --git a/packages/playwright-core/src/tools/backend/navigate.ts b/packages/playwright-core/src/tools/backend/navigate.ts index 460642cd92068..b0bf6e64a218b 100644 --- a/packages/playwright-core/src/tools/backend/navigate.ts +++ b/packages/playwright-core/src/tools/backend/navigate.ts @@ -32,9 +32,11 @@ const navigate = defineTool({ handle: async (context, params, response) => { const tab = await context.ensureTab(); - const url = await tab.checkUrlAndNavigate(params.url); - + // Always include the snapshot, even when navigation fails: navigating to a + // page that closes the last tab (e.g. chrome://extensions/) rejects goto, + // and we still want to render the resulting tab state gracefully. response.setIncludeSnapshot(); + const url = await tab.checkUrlAndNavigate(params.url); response.addCode(`await page.goto('${url}');`); }, }); diff --git a/packages/playwright-core/src/tools/backend/response.ts b/packages/playwright-core/src/tools/backend/response.ts index d28de344964bf..8b6a18caf2ab1 100644 --- a/packages/playwright-core/src/tools/backend/response.ts +++ b/packages/playwright-core/src/tools/backend/response.ts @@ -270,8 +270,13 @@ export class Response { addSection('Ran Playwright code', this._code, 'js'); // Render tab titles upon changes or when more than one tab. - const tabSnapshot = this._context.currentTab() ? await this._context.currentTabOrDie().captureSnapshot(this._includeSnapshotRoot, this._includeSnapshotDepth, this._includeSnapshotBoxes, this._clientWorkspace) : undefined; - const tabHeaders = await Promise.all(this._context.tabs().map(tab => tab.headerSnapshot())); + const currentTab = this._context.currentTab(); + const tabSnapshot = currentTab && !currentTab.page.isClosed() ? await currentTab.captureSnapshot(this._includeSnapshotRoot, this._includeSnapshotDepth, this._includeSnapshotBoxes, this._clientWorkspace) : undefined; + // A navigation may have closed the last tab (e.g. chrome://extensions/); the + // 'close' event that removes it from the tab list is async, so ignore tabs + // whose page has already closed to render a stable tab state. + const openTabs = this._context.tabs().filter(tab => !tab.page.isClosed()); + const tabHeaders = await Promise.all(openTabs.map(tab => tab.headerSnapshot())); if (this._includeSnapshot !== 'none' || tabHeaders.some(header => header.changed)) { if (tabHeaders.length !== 1) addSection('Open tabs', renderTabsMarkdown(tabHeaders));