From 78e1241ed779cd58bb63a650116b5a6751385f50 Mon Sep 17 00:00:00 2001 From: kamilbenkirane Date: Mon, 23 Feb 2026 16:37:45 +0100 Subject: [PATCH] fix: catch UnicodeDecodeError in _handle_error_response (#170) json.loads() raises UnicodeDecodeError (not JSONDecodeError) on binary bodies with BOM-like bytes (e.g. 0xff). Add it to the except tuple so the method falls through to the response.text fallback and raises httpx.HTTPStatusError as intended. --- src/celeste/client.py | 8 +++++++- tests/unit_tests/test_client.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/celeste/client.py b/src/celeste/client.py index d9f9ab10..f4b962d9 100644 --- a/src/celeste/client.py +++ b/src/celeste/client.py @@ -324,7 +324,13 @@ def _handle_error_response(self, response: httpx.Response) -> None: if not response.is_success: try: error_msg = response.json()["error"]["message"] - except (JSONDecodeError, KeyError, TypeError, IndexError): + except ( + JSONDecodeError, + KeyError, + TypeError, + IndexError, + UnicodeDecodeError, + ): error_msg = response.text or f"HTTP {response.status_code}" raise httpx.HTTPStatusError( diff --git a/tests/unit_tests/test_client.py b/tests/unit_tests/test_client.py index 5ec9e34c..1f7bb955 100644 --- a/tests/unit_tests/test_client.py +++ b/tests/unit_tests/test_client.py @@ -4,6 +4,7 @@ from enum import StrEnum from typing import Any, Unpack +import httpx import pytest from pydantic import SecretStr @@ -261,6 +262,36 @@ def parameter_mappers(cls) -> list[ParameterMapper]: assert transformed == expected_output +class TestHandleErrorResponse: + """Test ModalityClient._handle_error_response edge cases.""" + + @pytest.mark.parametrize( + "content", + [ + b"\xff random binary", + b"\xff\xfe\x00\x00 utf-32-le bom", + b"\x80\x81\x82\x83", + ], + ids=["0xff-byte", "utf-32-le-bom", "high-bytes"], + ) + def test_binary_error_body_raises_http_status_error( + self, text_model: Model, api_key: str, content: bytes + ) -> None: + """Binary error bodies produce HTTPStatusError, not UnicodeDecodeError (GH-170).""" + client = ConcreteModalityClient( + modality=Modality.TEXT, + model=text_model, + provider=text_model.provider, + auth=APIKey(secret=SecretStr(api_key)), + ) + response = httpx.Response( + 500, content=content, request=httpx.Request("POST", "https://api.test.com") + ) + + with pytest.raises(httpx.HTTPStatusError): + client._handle_error_response(response) + + class TestModalityClientStreaming: """Test ModalityClient._stream default behavior."""