Skip to content

Commit b94b8f5

Browse files
authored
fix: Handle readtimeout errors. (#305)
Fixes #249
1 parent e5d99ee commit b94b8f5

File tree

4 files changed

+45
-1
lines changed

4 files changed

+45
-1
lines changed

src/a2a/client/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
A2AClientError,
1111
A2AClientHTTPError,
1212
A2AClientJSONError,
13+
A2AClientTimeoutError,
1314
)
1415
from a2a.client.grpc_client import A2AGrpcClient
1516
from a2a.client.helpers import create_text_message_object
@@ -22,6 +23,7 @@
2223
'A2AClientError',
2324
'A2AClientHTTPError',
2425
'A2AClientJSONError',
26+
'A2AClientTimeoutError',
2527
'A2AGrpcClient',
2628
'AuthInterceptor',
2729
'ClientCallContext',

src/a2a/client/client.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@
1010
from httpx_sse import SSEError, aconnect_sse
1111
from pydantic import ValidationError
1212

13-
from a2a.client.errors import A2AClientHTTPError, A2AClientJSONError
13+
from a2a.client.errors import (
14+
A2AClientHTTPError,
15+
A2AClientJSONError,
16+
A2AClientTimeoutError,
17+
)
1418
from a2a.client.middleware import ClientCallContext, ClientCallInterceptor
1519
from a2a.types import (
1620
AgentCard,
@@ -340,6 +344,8 @@ async def _send_request(
340344
)
341345
response.raise_for_status()
342346
return response.json()
347+
except httpx.ReadTimeout as e:
348+
raise A2AClientTimeoutError('Client Request timed out') from e
343349
except httpx.HTTPStatusError as e:
344350
raise A2AClientHTTPError(e.response.status_code, str(e)) from e
345351
except json.JSONDecodeError as e:

src/a2a/client/errors.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,16 @@ def __init__(self, message: str):
3131
"""
3232
self.message = message
3333
super().__init__(f'JSON Error: {message}')
34+
35+
36+
class A2AClientTimeoutError(A2AClientError):
37+
"""Client exception for timeout errors during a request."""
38+
39+
def __init__(self, message: str):
40+
"""Initializes the A2AClientTimeoutError.
41+
42+
Args:
43+
message: A descriptive error message.
44+
"""
45+
self.message = message
46+
super().__init__(f'Timeout Error: {message}')

tests/client/test_client.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
A2AClient,
1515
A2AClientHTTPError,
1616
A2AClientJSONError,
17+
A2AClientTimeoutError,
1718
create_text_message_object,
1819
)
1920
from a2a.types import (
@@ -1266,3 +1267,25 @@ async def test_cancel_task_error_response(
12661267
mode='json', exclude_none=True
12671268
) == error_details.model_dump(exclude_none=True)
12681269
assert response.root.id == 'err_cancel_req'
1270+
1271+
@pytest.mark.asyncio
1272+
async def test_send_message_client_timeout(
1273+
self, mock_httpx_client: AsyncMock, mock_agent_card: MagicMock
1274+
):
1275+
mock_httpx_client.post.side_effect = httpx.ReadTimeout(
1276+
'Request timed out'
1277+
)
1278+
client = A2AClient(
1279+
httpx_client=mock_httpx_client, agent_card=mock_agent_card
1280+
)
1281+
1282+
params = MessageSendParams(
1283+
message=create_text_message_object(content='Hello')
1284+
)
1285+
1286+
request = SendMessageRequest(id=123, params=params)
1287+
1288+
with pytest.raises(A2AClientTimeoutError) as exc_info:
1289+
await client.send_message(request=request)
1290+
1291+
assert 'Request timed out' in str(exc_info.value)

0 commit comments

Comments
 (0)