diff --git a/packages/playwright-core/src/server/har/harTracer.ts b/packages/playwright-core/src/server/har/harTracer.ts index 930746ea08d03..96bf31724e4cc 100644 --- a/packages/playwright-core/src/server/har/harTracer.ts +++ b/packages/playwright-core/src/server/har/harTracer.ts @@ -265,9 +265,13 @@ export class HarTracer { const contentType = event.headers['content-type']; if (contentType) content.mimeType = contentType; - this._storeResponseContent(event.body, content, 'other'); - if (!this._options.omitSizes) - harEntry.response.bodySize = event.body?.length ?? 0; + // Redirect and auth-retry hops are destroyed before their body is read, so the + // sizes stay at the -1 "not available" sentinel instead of claiming zero bytes. + if (event.body) { + this._storeResponseContent(event.body, content, 'other'); + if (!this._options.omitSizes) + harEntry.response.bodySize = event.body.length; + } if (this._started) this._delegate.onEntryFinished(harEntry); @@ -541,12 +545,7 @@ export class HarTracer { this._delegate.onEntryStarted(harEntry); } - private _storeResponseContent(buffer: Buffer | undefined, content: har.Content, resourceType: string) { - if (!buffer) { - content.size = 0; - return; - } - + private _storeResponseContent(buffer: Buffer, content: har.Content, resourceType: string) { if (!this._options.omitSizes) content.size = buffer.length; diff --git a/tests/library/har.spec.ts b/tests/library/har.spec.ts index 4857202ccdc5e..abf80df23aa47 100644 --- a/tests/library/har.spec.ts +++ b/tests/library/har.spec.ts @@ -883,6 +883,10 @@ it('should include redirects from API request', async ({ contextFactory, server expect(redirect.timings).toBeDefined(); expect(json.timings).toBeDefined(); + + // The redirect body is never read, so its sizes stay unknown rather than claiming zero. + expect(redirect.response.bodySize).toBe(-1); + expect(redirect.response.content.size).toBe(-1); }); it('should not hang on resources served from cache', async ({ contextFactory, server, browserName, isBidi }, testInfo) => {