diff --git a/resend/async_request.py b/resend/async_request.py index 2634c15..c647ac9 100644 --- a/resend/async_request.py +++ b/resend/async_request.py @@ -170,7 +170,7 @@ async def make_request(self, url: str) -> Union[Dict[str, Any], List[Any]]: parsed_data["http_headers"] = dict(self._response_headers) # For list responses, return as-is (lists can't have headers key) return parsed_data - except json.JSONDecodeError: + except (json.JSONDecodeError, UnicodeDecodeError): raise_for_code_and_type( code=error_code, message="Failed to decode JSON response", diff --git a/resend/request.py b/resend/request.py index e7e2783..56d27c1 100644 --- a/resend/request.py +++ b/resend/request.py @@ -155,7 +155,7 @@ def make_request(self, url: str) -> Union[Dict[str, Any], List[Any]]: parsed_data["http_headers"] = dict(self._response_headers) # For list responses, return as-is (lists can't have headers key) return parsed_data - except json.JSONDecodeError: + except (json.JSONDecodeError, UnicodeDecodeError): raise_for_code_and_type( code=error_code, message="Failed to decode JSON response", diff --git a/resend/version.py b/resend/version.py index ddfd28c..917f42b 100644 --- a/resend/version.py +++ b/resend/version.py @@ -1,4 +1,4 @@ -__version__ = "2.43.0" +__version__ = "2.43.1" def get_version() -> str: diff --git a/tests/request_test.py b/tests/request_test.py index 56e2b3d..cde36de 100644 --- a/tests/request_test.py +++ b/tests/request_test.py @@ -1,3 +1,4 @@ +import asyncio import unittest from typing import Any, Dict from unittest.mock import AsyncMock, MagicMock, Mock, patch @@ -10,6 +11,45 @@ from resend.version import get_version +@pytest.mark.parametrize("content", [b'"\xff"', b'\xff\xfe{', b'not-json']) +@pytest.mark.parametrize("status_code", [200, 429, 502]) +class TestResponseDecodingErrors: + def test_sync_preserves_status_and_headers( + self, content: bytes, status_code: int + ) -> None: + headers = {"content-type": "application/json", "retry-after": "2"} + response = Mock(content=content, status_code=status_code, headers=headers) + req = request.Request[Dict[str, Any]]("/emails", {}, "get") + + with patch("resend.http_client_requests.requests.request", return_value=response): + with pytest.raises(ResendError) as error: + req.perform() + + assert error.value.code == (status_code if status_code >= 400 else 500) + assert error.value.headers == headers + assert error.value.message == "Failed to decode JSON response" + + def test_async_preserves_status_and_headers( + self, content: bytes, status_code: int + ) -> None: + from resend.async_request import AsyncRequest + from resend.http_client_httpx import HTTPXClient + + headers = {"content-type": "application/json", "retry-after": "2"} + response = Mock(content=content, status_code=status_code, headers=headers) + req = AsyncRequest[Dict[str, Any]]("/emails", {}, "get") + + with patch("resend.default_async_http_client", HTTPXClient()): + with patch("httpx.AsyncClient.request", new_callable=AsyncMock) as send: + send.return_value = response + with pytest.raises(ResendError) as error: + asyncio.run(req.perform()) + + assert error.value.code == (status_code if status_code >= 400 else 500) + assert error.value.headers == headers + assert error.value.message == "Failed to decode JSON response" + + class TestResendRequest(unittest.TestCase): @patch("resend.http_client_requests.requests.request") @patch("resend.api_key", new="test_key")