From 2b505c6661229f5eee7c9fa4d1a91b062e40259e Mon Sep 17 00:00:00 2001 From: Wali Reheman Date: Sun, 14 Jun 2026 09:06:53 -0400 Subject: [PATCH] fix(network): handle non-UTF-8 bytes in post_data without raising post_data.decode() used strict UTF-8 decoding which raises UnicodeDecodeError when the POST body contains invalid UTF-8 bytes. Use errors='replace' to return a best-effort string, matching the behavior of the JavaScript Playwright API. post_data_buffer remains the binary-safe API. Fixes https://github.com/microsoft/playwright-python/issues/3098 --- playwright/_impl/_network.py | 4 ++-- tests/async/test_page_network_request.py | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/playwright/_impl/_network.py b/playwright/_impl/_network.py index 852b5fac7..721ed5f07 100644 --- a/playwright/_impl/_network.py +++ b/playwright/_impl/_network.py @@ -209,10 +209,10 @@ async def sizes(self) -> RequestSizes: def post_data(self) -> Optional[str]: data = self._fallback_overrides.post_data_buffer if data: - return data.decode() + return data.decode("utf-8", errors="replace") base64_post_data = self._initializer.get("postData") if base64_post_data is not None: - return base64.b64decode(base64_post_data).decode() + return base64.b64decode(base64_post_data).decode("utf-8", errors="replace") return None @property diff --git a/tests/async/test_page_network_request.py b/tests/async/test_page_network_request.py index bab4d5fd0..04049c857 100644 --- a/tests/async/test_page_network_request.py +++ b/tests/async/test_page_network_request.py @@ -82,3 +82,25 @@ async def test_existing_response_should_return_none_before_response_arrives( assert captured # When the "request" event fires, the response has not been received yet. assert captured[0] is None + + +async def test_post_data_non_utf8_bytes_does_not_raise( + page: Page, server: Server +) -> None: + # Regression: non-UTF-8 bytes in POST body should not crash post_data + # post_data_buffer returns the raw bytes; post_data should return a + # best-effort decoded string + async with page.expect_request("**/upload") as request_info: + await page.evaluate( + "() => fetch('/upload', {" + "method: 'POST'," + "body: new Uint8Array([255, 254, 0, 1])" + "})" + ) + request = await request_info.value + # post_data must not raise UnicodeDecodeError + post_data_str = request.post_data + assert isinstance(post_data_str, str) + # post_data_buffer returns the exact bytes + assert request.post_data_buffer == b'\xff\xfe\x00\x01' +