diff --git a/packages/playwright-core/src/server/network.ts b/packages/playwright-core/src/server/network.ts index 457efb571716f..e44a4e6c17325 100644 --- a/packages/playwright-core/src/server/network.ts +++ b/packages/playwright-core/src/server/network.ts @@ -16,6 +16,8 @@ import { ManualPromise } from '@isomorphic/manualPromise'; import { assert } from '@isomorphic/assert'; +import { rewriteErrorMessage } from '@isomorphic/stackTrace'; +import { isProtocolError } from './protocolError'; import { BrowserContext } from './browserContext'; import { APIRequestContext } from './fetch'; import { SdkObject } from './instrumentation'; @@ -651,7 +653,13 @@ export class Response extends SdkObject { const { body, isBase64 } = this._request._responseBodyOverride; return Buffer.from(body, isBase64 ? 'base64' : 'utf-8'); } - return this._getResponseBodyCallback(); + try { + return await this._getResponseBodyCallback(); + } catch (e) { + if (isProtocolError(e) && e.type === 'error') + rewriteErrorMessage(e, e.message + '\nResponse body is not available for a response that was navigated away from. Read response.body() before triggering any navigation.'); + throw e; + } }); } return this._contentPromise; diff --git a/tests/page/page-network-response.spec.ts b/tests/page/page-network-response.spec.ts index 76a56152387e6..a968d5843f3fa 100644 --- a/tests/page/page-network-response.spec.ts +++ b/tests/page/page-network-response.spec.ts @@ -450,3 +450,17 @@ it('Response.formData() should parse multipart/form-data in page context', async expect(result.filename).toBe('test.txt'); expect(result.fileContent).toBe('hello'); }); + +it('should give a readable error when response.body() races with navigation', async ({ page, server, browserName }) => { + it.skip(browserName === 'firefox', 'Firefox has a separate eviction error path'); + it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/41512' }); + const [response] = await Promise.all([ + page.waitForResponse(server.EMPTY_PAGE), + page.goto(server.EMPTY_PAGE), + ]); + // Navigate away — the browser frees the network resource from the first page load + await page.goto(server.PREFIX + '/title.html'); + const error = await response.body().catch(e => e); + expect(error).toBeInstanceOf(Error); + expect(error.message).toContain('navigated away'); +});