From 580df89ae6456f6813a37554a55510a551304adb Mon Sep 17 00:00:00 2001 From: ayaangazali Date: Sun, 23 Aug 2026 10:36:02 -0700 Subject: [PATCH] fix(har): do not report zero sizes for bodies that were never read A redirect or basic-auth retry hop made through APIRequestContext is destroyed before its response body is read, so the body never reaches the tracer. It still recorded bodySize 0 and content.size 0, which claims the server sent nothing. The result contradicts itself: an entry can carry Content-Length 40 in its own headers and bodySize 0 next to it. HAR 1.2 uses -1 for a size that is not available, which is what these entries start as, and what the browser path already leaves them as when the body cannot be read. Only record the sizes when a body was actually captured. That makes the undefined-buffer branch of _storeResponseContent unreachable, so it goes away and the parameter stops being optional. --- .../playwright-core/src/server/har/harTracer.ts | 17 ++++++++--------- tests/library/har.spec.ts | 4 ++++ 2 files changed, 12 insertions(+), 9 deletions(-) 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) => {