From 3b03f68852474706e17de997483daa8bcc28fa8a Mon Sep 17 00:00:00 2001 From: Kayvan Zahiri <123409429+Kayvan-Zahiri@users.noreply.github.com> Date: Sun, 18 Oct 2026 15:58:01 -0700 Subject: [PATCH] fix: raise SDK errors for unexpected HTTP error bodies --- src/linkup/_client.py | 14 +++++++- tests/unit/unexpected_error_test.py | 52 +++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 tests/unit/unexpected_error_test.py diff --git a/src/linkup/_client.py b/src/linkup/_client.py index c249259..e0b5d72 100644 --- a/src/linkup/_client.py +++ b/src/linkup/_client.py @@ -1370,7 +1370,19 @@ async def _async_handle_x402_payment( return retry_response def _raise_linkup_error(self, response: httpx.Response) -> None: - error_data = response.json() + try: + error_data = response.json() + except ValueError: + error_data = None + + if not isinstance(error_data, dict): + error_data = {} + error_data = cast("dict[str, Any]", error_data) + if not isinstance(error_data.get("error"), dict): + raise LinkupUnknownError( + f"The Linkup API returned an unknown error ({response.status_code}).\n" + f"Original error message: ({response.text})." + ) if "error" in error_data: error = error_data["error"] diff --git a/tests/unit/unexpected_error_test.py b/tests/unit/unexpected_error_test.py new file mode 100644 index 0000000..4a12ca1 --- /dev/null +++ b/tests/unit/unexpected_error_test.py @@ -0,0 +1,52 @@ +import httpx +import pytest +from pytest_mock import MockerFixture + +import linkup + + +@pytest.mark.parametrize( + "body", + [ + b"Bad Gateway", + b"", + b"Bad Gateway", + b"null", + b"[]", + b'{"message": "upstream unavailable"}', + b'{"error": "upstream unavailable"}', + b'{"error": null}', + b'{"error": {"message": "upstream unavailable"}}', + ], +) +def test_unexpected_error_response( + body: bytes, mocker: MockerFixture, client: linkup.Client +) -> None: + mocker.patch("httpx.Client.request", return_value=httpx.Response(502, content=body)) + + with pytest.raises(linkup.UnknownError, match="502"): + client.search(query="test", depth="standard", output_type="searchResults") + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "body", + [ + b"Bad Gateway", + b"", + b"Bad Gateway", + b"null", + b"[]", + b'{"message": "upstream unavailable"}', + b'{"error": "upstream unavailable"}', + b'{"error": null}', + b'{"error": {"message": "upstream unavailable"}}', + ], +) +async def test_async_unexpected_error_response( + body: bytes, mocker: MockerFixture, client: linkup.Client +) -> None: + mocker.patch("httpx.AsyncClient.request", return_value=httpx.Response(502, content=body)) + + with pytest.raises(linkup.UnknownError, match="502"): + await client.async_search(query="test", depth="standard", output_type="searchResults")