From 68c69a68ab69ee1dbd63b0135a673ba4dd2867ef Mon Sep 17 00:00:00 2001 From: Krishna Thota Date: Mon, 14 Jul 2025 14:57:24 -0700 Subject: [PATCH 1/2] Fix: Handle readtimeout errors. Fixes #249 --- src/a2a/client/__init__.py | 2 ++ src/a2a/client/client.py | 8 +++++++- src/a2a/client/errors.py | 13 +++++++++++++ tests/client/test_client.py | 21 +++++++++++++++++++++ 4 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/a2a/client/__init__.py b/src/a2a/client/__init__.py index 3d673b31e..393d85ec9 100644 --- a/src/a2a/client/__init__.py +++ b/src/a2a/client/__init__.py @@ -10,6 +10,7 @@ A2AClientError, A2AClientHTTPError, A2AClientJSONError, + A2AClientTimeoutError, ) from a2a.client.grpc_client import A2AGrpcClient from a2a.client.helpers import create_text_message_object @@ -22,6 +23,7 @@ 'A2AClientError', 'A2AClientHTTPError', 'A2AClientJSONError', + 'A2AClientTimeoutError', 'A2AGrpcClient', 'AuthInterceptor', 'ClientCallContext', diff --git a/src/a2a/client/client.py b/src/a2a/client/client.py index 66a1e49bb..66dfe0a4b 100644 --- a/src/a2a/client/client.py +++ b/src/a2a/client/client.py @@ -10,7 +10,11 @@ from httpx_sse import SSEError, aconnect_sse from pydantic import ValidationError -from a2a.client.errors import A2AClientHTTPError, A2AClientJSONError +from a2a.client.errors import ( + A2AClientHTTPError, + A2AClientJSONError, + A2AClientTimeoutError, +) from a2a.client.middleware import ClientCallContext, ClientCallInterceptor from a2a.types import ( AgentCard, @@ -340,6 +344,8 @@ async def _send_request( ) response.raise_for_status() return response.json() + except httpx.ReadTimeout as e: + raise A2AClientTimeoutError('Client Request timed out') from e except httpx.HTTPStatusError as e: raise A2AClientHTTPError(e.response.status_code, str(e)) from e except json.JSONDecodeError as e: diff --git a/src/a2a/client/errors.py b/src/a2a/client/errors.py index da02e5826..5fe5512a8 100644 --- a/src/a2a/client/errors.py +++ b/src/a2a/client/errors.py @@ -31,3 +31,16 @@ def __init__(self, message: str): """ self.message = message super().__init__(f'JSON Error: {message}') + + +class A2AClientTimeoutError(A2AClientError): + """Client exception for timeout errors during a request.""" + + def __init__(self, message: str): + """Initializes the A2AClientTimeoutError. + + Args: + message: A descriptive error message. + """ + self.message = message + super().__init__(f'Timeout Error: {message}') diff --git a/tests/client/test_client.py b/tests/client/test_client.py index 5b6e94912..9e69b72d0 100644 --- a/tests/client/test_client.py +++ b/tests/client/test_client.py @@ -14,6 +14,7 @@ A2AClient, A2AClientHTTPError, A2AClientJSONError, + A2AClientTimeoutError, create_text_message_object, ) from a2a.types import ( @@ -1266,3 +1267,23 @@ async def test_cancel_task_error_response( mode='json', exclude_none=True ) == error_details.model_dump(exclude_none=True) assert response.root.id == 'err_cancel_req' + + @pytest.mark.asyncio + async def test_send_message_client_timeout( + self, mock_httpx_client: AsyncMock, mock_agent_card: MagicMock + ): + mock_httpx_client.post.side_effect = httpx.ReadTimeout( + 'Request timed out' + ) + client = A2AClient( + httpx_client=mock_httpx_client, agent_card=mock_agent_card + ) + + params = MessageSendParams( + message=create_text_message_object(content='Hello') + ) + + request = SendMessageRequest(id=123, params=params) + + with pytest.raises(A2AClientTimeoutError): + await client.send_message(request=request) From 1af9cde9a4b628a6f74a572257969a3cbc7ced52 Mon Sep 17 00:00:00 2001 From: kthota-g Date: Mon, 14 Jul 2025 15:00:35 -0700 Subject: [PATCH 2/2] Update tests/client/test_client.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- tests/client/test_client.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/client/test_client.py b/tests/client/test_client.py index 9e69b72d0..00ab8796e 100644 --- a/tests/client/test_client.py +++ b/tests/client/test_client.py @@ -1285,5 +1285,7 @@ async def test_send_message_client_timeout( request = SendMessageRequest(id=123, params=params) - with pytest.raises(A2AClientTimeoutError): + with pytest.raises(A2AClientTimeoutError) as exc_info: await client.send_message(request=request) + + assert 'Request timed out' in str(exc_info.value)