22import logging
33
44from collections .abc import Awaitable , Callable , Coroutine
5- from typing import TYPE_CHECKING , Any , cast
5+ from typing import TYPE_CHECKING , Any
66
77
88if TYPE_CHECKING :
1717
1818from google .protobuf .json_format import ParseError
1919
20- from a2a .server .jsonrpc_models import (
21- InternalError as JSONRPCInternalError ,
22- )
23- from a2a .server .jsonrpc_models import (
24- JSONParseError ,
25- JSONRPCError ,
26- )
2720from a2a .utils .errors import (
21+ A2A_REST_ERROR_MAPPING ,
2822 A2AError ,
29- ContentTypeNotSupportedError ,
30- ExtendedAgentCardNotConfiguredError ,
31- ExtensionSupportRequiredError ,
3223 InternalError ,
33- InvalidAgentResponseError ,
34- InvalidParamsError ,
35- InvalidRequestError ,
36- MethodNotFoundError ,
37- PushNotificationNotSupportedError ,
38- TaskNotCancelableError ,
39- TaskNotFoundError ,
40- UnsupportedOperationError ,
41- VersionNotSupportedError ,
4224)
4325
4426
4527logger = logging .getLogger (__name__ )
4628
47- _A2AErrorType = (
48- type [JSONRPCError ]
49- | type [JSONParseError ]
50- | type [InvalidRequestError ]
51- | type [MethodNotFoundError ]
52- | type [InvalidParamsError ]
53- | type [InternalError ]
54- | type [JSONRPCInternalError ]
55- | type [TaskNotFoundError ]
56- | type [TaskNotCancelableError ]
57- | type [PushNotificationNotSupportedError ]
58- | type [UnsupportedOperationError ]
59- | type [ContentTypeNotSupportedError ]
60- | type [InvalidAgentResponseError ]
61- | type [ExtendedAgentCardNotConfiguredError ]
62- | type [ExtensionSupportRequiredError ]
63- | type [VersionNotSupportedError ]
64- )
65-
66- A2AErrorToHttpStatus : dict [_A2AErrorType , int ] = {
67- JSONRPCError : 500 ,
68- JSONParseError : 400 ,
69- InvalidRequestError : 400 ,
70- MethodNotFoundError : 404 ,
71- InvalidParamsError : 422 ,
72- InternalError : 500 ,
73- JSONRPCInternalError : 500 ,
74- TaskNotFoundError : 404 ,
75- TaskNotCancelableError : 409 ,
76- PushNotificationNotSupportedError : 501 ,
77- UnsupportedOperationError : 501 ,
78- ContentTypeNotSupportedError : 415 ,
79- InvalidAgentResponseError : 502 ,
80- ExtendedAgentCardNotConfiguredError : 400 ,
81- ExtensionSupportRequiredError : 400 ,
82- VersionNotSupportedError : 400 ,
83- }
84-
8529
8630def rest_error_handler (
8731 func : Callable [..., Awaitable [Response ]],
@@ -93,8 +37,8 @@ async def wrapper(*args: Any, **kwargs: Any) -> Response:
9337 try :
9438 return await func (* args , ** kwargs )
9539 except A2AError as error :
96- http_code = A2AErrorToHttpStatus .get (
97- cast ( '_A2AErrorType' , type (error )), 500
40+ http_code , grpc_status , reason = A2A_REST_ERROR_MAPPING .get (
41+ type (error ), ( 500 , 'INTERNAL' , 'INTERNAL_ERROR' )
9842 )
9943
10044 log_level = (
@@ -107,32 +51,64 @@ async def wrapper(*args: Any, **kwargs: Any) -> Response:
10751 "Request error: Code=%s, Message='%s'%s" ,
10852 getattr (error , 'code' , 'N/A' ),
10953 getattr (error , 'message' , str (error )),
110- ', Data=' + str (getattr (error , 'data' , '' ))
111- if getattr (error , 'data' , None )
112- else '' ,
54+ f', Data={ error .data } ' if hasattr (error , 'data' ) else '' ,
11355 )
114- # TODO(#722): Standardize error response format.
56+
57+ # SECURITY WARNING: Data attached to A2AError.data is serialized unaltered and exposed publicly to the client in the REST API response.
58+ metadata = getattr (error , 'data' , None ) or {}
59+
11560 return JSONResponse (
11661 content = {
117- 'message' : getattr (error , 'message' , str (error )),
118- 'type' : type (error ).__name__ ,
62+ 'error' : {
63+ 'code' : http_code ,
64+ 'status' : grpc_status ,
65+ 'message' : getattr (error , 'message' , str (error )),
66+ 'details' : [
67+ {
68+ '@type' : 'type.googleapis.com/google.rpc.ErrorInfo' ,
69+ 'reason' : reason ,
70+ 'domain' : 'a2a-protocol.org' ,
71+ 'metadata' : metadata ,
72+ }
73+ ],
74+ }
11975 },
12076 status_code = http_code ,
77+ media_type = 'application/json' ,
12178 )
12279 except ParseError as error :
12380 logger .warning ('Parse error: %s' , str (error ))
12481 return JSONResponse (
12582 content = {
126- 'message' : str (error ),
127- 'type' : 'ParseError' ,
83+ 'error' : {
84+ 'code' : 400 ,
85+ 'status' : 'INVALID_ARGUMENT' ,
86+ 'message' : str (error ),
87+ 'details' : [
88+ {
89+ '@type' : 'type.googleapis.com/google.rpc.ErrorInfo' ,
90+ 'reason' : 'INVALID_REQUEST' ,
91+ 'domain' : 'a2a-protocol.org' ,
92+ 'metadata' : {},
93+ }
94+ ],
95+ }
12896 },
12997 status_code = 400 ,
98+ media_type = 'application/json' ,
13099 )
131100 except Exception :
132101 logger .exception ('Unknown error occurred' )
133102 return JSONResponse (
134- content = {'message' : 'unknown exception' , 'type' : 'Exception' },
103+ content = {
104+ 'error' : {
105+ 'code' : 500 ,
106+ 'status' : 'INTERNAL' ,
107+ 'message' : 'unknown exception' ,
108+ }
109+ },
135110 status_code = 500 ,
111+ media_type = 'application/json' ,
136112 )
137113
138114 return wrapper
0 commit comments