2121 A2A_REST_ERROR_MAPPING ,
2222 A2AError ,
2323 InternalError ,
24+ RestErrorMap ,
2425)
2526
2627
2728logger = logging .getLogger (__name__ )
2829
2930
31+ def _build_error_payload (
32+ code : int ,
33+ status : str ,
34+ message : str ,
35+ reason : str | None = None ,
36+ metadata : dict [str , Any ] | None = None ,
37+ ) -> dict [str , Any ]:
38+ """Helper function to build the JSON error payload."""
39+ payload : dict [str , Any ] = {
40+ 'code' : code ,
41+ 'status' : status ,
42+ 'message' : message ,
43+ }
44+ if reason :
45+ payload ['details' ] = [
46+ {
47+ '@type' : 'type.googleapis.com/google.rpc.ErrorInfo' ,
48+ 'reason' : reason ,
49+ 'domain' : 'a2a-protocol.org' ,
50+ 'metadata' : metadata if metadata is not None else {},
51+ }
52+ ]
53+ return {'error' : payload }
54+
55+
3056def rest_error_handler (
3157 func : Callable [..., Awaitable [Response ]],
3258) -> Callable [..., Awaitable [Response ]]:
@@ -37,9 +63,12 @@ async def wrapper(*args: Any, **kwargs: Any) -> Response:
3763 try :
3864 return await func (* args , ** kwargs )
3965 except A2AError as error :
40- http_code , grpc_status , reason = A2A_REST_ERROR_MAPPING .get (
41- type (error ), (500 , 'INTERNAL' , 'INTERNAL_ERROR' )
66+ mapping = A2A_REST_ERROR_MAPPING .get (
67+ type (error ), RestErrorMap (500 , 'INTERNAL' , 'INTERNAL_ERROR' )
4268 )
69+ http_code = mapping .http_code
70+ grpc_status = mapping .grpc_status
71+ reason = mapping .reason
4372
4473 log_level = (
4574 logging .ERROR
@@ -51,62 +80,44 @@ async def wrapper(*args: Any, **kwargs: Any) -> Response:
5180 "Request error: Code=%s, Message='%s'%s" ,
5281 getattr (error , 'code' , 'N/A' ),
5382 getattr (error , 'message' , str (error )),
54- f', Data={ error .data } ' if hasattr ( error , ' data' ) else '' ,
83+ f', Data={ error .data } ' if error . data else '' ,
5584 )
5685
5786 # SECURITY WARNING: Data attached to A2AError.data is serialized unaltered and exposed publicly to the client in the REST API response.
5887 metadata = getattr (error , 'data' , None ) or {}
5988
6089 return JSONResponse (
61- content = {
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- }
75- },
90+ content = _build_error_payload (
91+ code = http_code ,
92+ status = grpc_status ,
93+ message = getattr (error , 'message' , str (error )),
94+ reason = reason ,
95+ metadata = metadata ,
96+ ),
7697 status_code = http_code ,
7798 media_type = 'application/json' ,
7899 )
79100 except ParseError as error :
80101 logger .warning ('Parse error: %s' , str (error ))
81102 return JSONResponse (
82- content = {
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- }
96- },
103+ content = _build_error_payload (
104+ code = 400 ,
105+ status = 'INVALID_ARGUMENT' ,
106+ message = str (error ),
107+ reason = 'INVALID_REQUEST' ,
108+ metadata = {},
109+ ),
97110 status_code = 400 ,
98111 media_type = 'application/json' ,
99112 )
100113 except Exception :
101114 logger .exception ('Unknown error occurred' )
102115 return JSONResponse (
103- content = {
104- 'error' : {
105- 'code' : 500 ,
106- 'status' : 'INTERNAL' ,
107- 'message' : 'unknown exception' ,
108- }
109- },
116+ content = _build_error_payload (
117+ code = 500 ,
118+ status = 'INTERNAL' ,
119+ message = 'unknown exception' ,
120+ ),
110121 status_code = 500 ,
111122 media_type = 'application/json' ,
112123 )
@@ -134,9 +145,7 @@ async def wrapper(*args: Any, **kwargs: Any) -> Any:
134145 "Request error: Code=%s, Message='%s'%s" ,
135146 getattr (error , 'code' , 'N/A' ),
136147 getattr (error , 'message' , str (error )),
137- ', Data=' + str (getattr (error , 'data' , '' ))
138- if getattr (error , 'data' , None )
139- else '' ,
148+ f', Data={ error .data } ' if error .data else '' ,
140149 )
141150 # Since the stream has started, we can't return a JSONResponse.
142151 # Instead, we run the error handling logic (provides logging)
0 commit comments