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' +