From 46122596a7dbda4b8b8a7d7d78c86bda43b0e221 Mon Sep 17 00:00:00 2001 From: Mark Larah Date: Sun, 21 Dec 2025 13:27:02 -0600 Subject: [PATCH] Catch utf8 decoding errors explicitly. Previously, utf8 decode errors bubbled up into json.JSONDecodeError error handling, which triggered a bug in py310 specifically: - The failing test was test_http_multipart_invalid_utf8 (test #17) - It sends invalid utf8 bytes (\xff\xfe) in the multipart response - aiohttp's part.text() raises a UnicodeDecodeError - The HTTP multipart transport wasn't catching UnicodeDecodeError, only json.JSONDecodeError - When pytest tried to format the uncaught underlying UnicodeDecodeError, the exceptiongroup package in py310 hit an internal bug: File "/home/runner/work/gql/gql/.tox/pypy3/lib/pypy3.10/site-packages/exceptiongroup/_formatting.py", line 329, in format_exception_only yield _format_final_exc_line(stype, self._str) File "/home/runner/work/gql/gql/.tox/pypy3/lib/pypy3.10/site-packages/exceptiongroup/_formatting.py", line 33, in _format_final_exc_line line = f"{etype}: {valuestr}\n" SystemError: unexpected internal exception (please report a bug): Catching the utf8 decoding error specifically does actually improve error handling anyway since it gives a more specific warning to the user imo. --- gql/transport/http_multipart_transport.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gql/transport/http_multipart_transport.py b/gql/transport/http_multipart_transport.py index 81dd1e51..3496ac7e 100644 --- a/gql/transport/http_multipart_transport.py +++ b/gql/transport/http_multipart_transport.py @@ -303,10 +303,11 @@ async def _parse_multipart_part( errors=payload.get("errors"), extensions=payload.get("extensions"), ) + except UnicodeDecodeError as e: + log.warning(f"Skipping part - failed to decode as UTF-8: {e}") + return None except json.JSONDecodeError as e: - log.warning( - f"Failed to parse JSON: {e}, body: {body[:100] if body else ''}" - ) + log.warning(f"Skipping part - failed to parse JSON: {e}") return None async def execute(