From ff863b44f2bf13eb5032af0fb0d2a27c2a3e1123 Mon Sep 17 00:00:00 2001 From: cjumel Date: Tue, 30 Jun 2026 15:33:43 +0200 Subject: [PATCH] fix: handle unsupported task types --- src/linkup/__init__.py | 4 ++++ src/linkup/_client.py | 10 ++++++++- src/linkup/_errors.py | 6 ++++++ tests/unit/client_test.py | 43 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/linkup/__init__.py b/src/linkup/__init__.py index b47680f..09574fe 100644 --- a/src/linkup/__init__.py +++ b/src/linkup/__init__.py @@ -15,6 +15,7 @@ LinkupTimeoutError, LinkupTooManyRequestsError, LinkupUnknownError, + LinkupUnsupportedTaskTypeError, ) from ._types import ( JSONObject, @@ -78,6 +79,7 @@ TimeoutError = LinkupTimeoutError # noqa: A001 TooManyRequestsError = LinkupTooManyRequestsError UnknownError = LinkupUnknownError +UnsupportedTaskTypeError = LinkupUnsupportedTaskTypeError __all__ = [ "AuthenticationError", @@ -130,6 +132,7 @@ "LinkupTimeoutError", "LinkupTooManyRequestsError", "LinkupUnknownError", + "LinkupUnsupportedTaskTypeError", "NoResultError", "PaymentRequiredError", "ResearchTask", @@ -153,5 +156,6 @@ "TimeoutError", "TooManyRequestsError", "UnknownError", + "UnsupportedTaskTypeError", "__version__", ] diff --git a/src/linkup/_client.py b/src/linkup/_client.py index f42e55c..c6b1647 100644 --- a/src/linkup/_client.py +++ b/src/linkup/_client.py @@ -25,6 +25,7 @@ LinkupTimeoutError, LinkupTooManyRequestsError, LinkupUnknownError, + LinkupUnsupportedTaskTypeError, ) from ._types import ( JSONObject, @@ -1341,6 +1342,11 @@ def _raise_linkup_error(self, response: httpx.Response) -> None: f"Original error message: {error_msg}." ) if response.status_code == 403: + if code == "TASK_TYPE_NOT_SUPPORTED": + raise LinkupUnsupportedTaskTypeError( + "The Linkup API returned an unsupported task type error (403). \n" + f"Original error message: {error_msg}." + ) raise LinkupAuthenticationError( "The Linkup API returned an authorization error (403). Make sure your API " "key is valid.\n" @@ -1692,7 +1698,9 @@ def _parse_task(self, task_data: dict[str, Any]) -> LinkupTask: if task_type == "research": return self._parse_research_task(task_data) - raise ValueError(f"Unexpected task type value: '{task_type}'") + raise LinkupUnsupportedTaskTypeError( + f"The Linkup API returned an unsupported task type '{task_type}'." + ) def _parse_research_tasks_page(self, response_data: dict[str, Any]) -> LinkupResearchTasksPage: return LinkupResearchTasksPage.model_validate( diff --git a/src/linkup/_errors.py b/src/linkup/_errors.py index fec1623..d7e0e26 100644 --- a/src/linkup/_errors.py +++ b/src/linkup/_errors.py @@ -136,3 +136,9 @@ class LinkupUnknownError(Exception): """Unknown error, raised when the Linkup API returns an unknown status code.""" pass + + +class LinkupUnsupportedTaskTypeError(Exception): + """Unsupported task type error, raised when a task type is not supported by this SDK.""" + + pass diff --git a/tests/unit/client_test.py b/tests/unit/client_test.py index 50ec541..36f7750 100644 --- a/tests/unit/client_test.py +++ b/tests/unit/client_test.py @@ -474,6 +474,19 @@ async def test_async_search_structured_output_requires_schema(client: linkup.Cli """, linkup.AuthenticationError, ), + ( + 403, + b""" + { + "error": { + "code": "TASK_TYPE_NOT_SUPPORTED", + "message": "Extract tasks are not enabled for this organization.", + "details": [] + } + } + """, + linkup.UnsupportedTaskTypeError, + ), ( 401, b""" @@ -1492,6 +1505,36 @@ def test_list_tasks(mocker: MockerFixture, client: linkup.Client) -> None: assert tasks_page.quota.in_flight == 1 +def test_get_task_unsupported_task_type(mocker: MockerFixture, client: linkup.Client) -> None: + mocker.patch( + "httpx.Client.request", + return_value=Response( + status_code=200, + content=b""" + { + "createdAt": "2026-05-18T00:00:00.000Z", + "error": null, + "id": "extract-task", + "input": { + "q": "query", + "url": "https://example.com" + }, + "output": null, + "status": "pending", + "type": "extract", + "updatedAt": "2026-05-18T00:00:00.000Z" + } + """, + ), + ) + + with pytest.raises( + linkup.UnsupportedTaskTypeError, + match="unsupported task type 'extract'", + ): + client.get_task("extract-task") + + def test_get_task_structured_search_output_keeps_search_results_shape_raw( mocker: MockerFixture, client: linkup.Client ) -> None: