Skip to content

Commit bbd09f2

Browse files
authored
fix: handle parsing error in REST (#806)
Add tests and update REST error handler to catch `ParseError` thrown by ProtoJSON, currently it produces 500.
1 parent 08c491e commit bbd09f2

File tree

2 files changed

+80
-0
lines changed

2 files changed

+80
-0
lines changed

src/a2a/utils/error_handlers.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
Response = Any
1616

1717

18+
from google.protobuf.json_format import ParseError
19+
1820
from a2a.server.jsonrpc_models import (
1921
InternalError as JSONRPCInternalError,
2022
)
@@ -111,6 +113,15 @@ async def wrapper(*args: Any, **kwargs: Any) -> Response:
111113
},
112114
status_code=http_code,
113115
)
116+
except ParseError as error:
117+
logger.warning('Parse error: %s', str(error))
118+
return JSONResponse(
119+
content={
120+
'message': str(error),
121+
'type': 'ParseError',
122+
},
123+
status_code=400,
124+
)
114125
except Exception:
115126
logger.exception('Unknown error occurred')
116127
return JSONResponse(

tests/integration/test_client_server_integration.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -773,3 +773,72 @@ async def test_client_get_signed_base_and_extended_cards(
773773

774774
if hasattr(transport, 'close'):
775775
await transport.close()
776+
777+
778+
@pytest.mark.asyncio
779+
async def test_jsonrpc_malformed_payload(jsonrpc_setup: TransportSetup) -> None:
780+
"""Integration test to verify that JSON-RPC malformed payloads don't return 500."""
781+
transport = jsonrpc_setup.transport
782+
client = transport.httpx_client
783+
url = transport.url
784+
785+
# 1. Invalid JSON
786+
response = await client.post(url, content='not a json')
787+
assert response.status_code == 200
788+
assert response.json()['error']['code'] == -32700 # Parse error
789+
790+
# 2. Wrong types in params
791+
response = await client.post(
792+
url,
793+
json={
794+
'jsonrpc': '2.0',
795+
'method': 'SendMessage',
796+
'params': {'message': 'should be an object'},
797+
'id': 1,
798+
},
799+
)
800+
assert response.status_code == 200
801+
assert response.json()['error']['code'] == -32602 # Invalid params
802+
803+
await transport.close()
804+
805+
806+
@pytest.mark.asyncio
807+
@pytest.mark.parametrize(
808+
'method, path, request_kwargs',
809+
[
810+
pytest.param(
811+
'POST',
812+
'/message:send',
813+
{'content': 'not a json'},
814+
id='invalid-json',
815+
),
816+
pytest.param(
817+
'POST',
818+
'/message:send',
819+
{'json': {'message': 'should be an object'}},
820+
id='wrong-body-type',
821+
),
822+
pytest.param(
823+
'GET',
824+
'/tasks',
825+
{'params': {'historyLength': 'not_an_int'}},
826+
id='wrong-query-param-type',
827+
),
828+
],
829+
)
830+
async def test_rest_malformed_payload(
831+
rest_setup: TransportSetup,
832+
method: str,
833+
path: str,
834+
request_kwargs: dict[str, Any],
835+
) -> None:
836+
"""Integration test to verify that REST malformed payloads don't return 500."""
837+
transport = rest_setup.transport
838+
client = transport.httpx_client
839+
url = transport.url
840+
841+
response = await client.request(method, f'{url}{path}', **request_kwargs)
842+
assert response.status_code == 400
843+
844+
await transport.close()

0 commit comments

Comments
 (0)