2828 GetTaskPushNotificationConfigRequest ,
2929 GetTaskRequest ,
3030 InternalError ,
31+ InvalidParamsError ,
3132 InvalidRequestError ,
3233 JSONParseError ,
3334 JSONRPCError ,
3435 JSONRPCErrorResponse ,
3536 JSONRPCRequest ,
3637 JSONRPCResponse ,
38+ MethodNotFoundError ,
3739 ListTaskPushNotificationConfigRequest ,
3840 SendMessageRequest ,
3941 SendStreamingMessageRequest ,
@@ -267,17 +269,102 @@ async def _handle_requests(self, request: Request) -> Response: # noqa: PLR0911
267269 body = await request .json ()
268270 if isinstance (body , dict ):
269271 request_id = body .get ('id' )
272+ # Ensure request_id is valid for JSON-RPC response (str/int/None only)
273+ if request_id is not None and not isinstance (
274+ request_id , (str , int )
275+ ):
276+ request_id = None
277+ # Treat very large payloads as invalid request (-32600) before routing
278+ with contextlib .suppress (Exception ):
279+ content_length = int (request .headers .get ('content-length' , '0' ))
280+ if content_length and content_length > 1_000_000 :
281+ return self ._generate_error_response (
282+ request_id ,
283+ A2AError (
284+ root = InvalidRequestError (
285+ message = 'Payload too large'
286+ )
287+ ),
288+ )
289+ logger .info (f'**********Request body: { body } ' )
290+ # 1) Validate base JSON-RPC structure only (-32600 on failure)
291+ try :
292+ base_request = JSONRPCRequest .model_validate (body )
293+ except ValidationError as e :
294+ traceback .print_exc ()
295+ return self ._generate_error_response (
296+ request_id ,
297+ A2AError (
298+ root = InvalidRequestError (data = json .loads (e .json ()))
299+ ),
300+ )
270301
271- # First, validate the basic JSON-RPC structure. This is crucial
272- # because the A2ARequest model is a discriminated union where some
273- # request types have default values for the 'method' field
274- JSONRPCRequest .model_validate (body )
275-
276- a2a_request = A2ARequest .model_validate (body )
302+ # 2) Route by method name; unknown -> -32601, known -> validate params (-32602 on failure)
303+ method = base_request .method
304+ try :
305+ match method :
306+ case 'message/send' :
307+ specific_request = SendMessageRequest .model_validate (
308+ body
309+ )
310+ case 'message/stream' :
311+ specific_request = (
312+ SendStreamingMessageRequest .model_validate (body )
313+ )
314+ case 'tasks/get' :
315+ specific_request = GetTaskRequest .model_validate (body )
316+ case 'tasks/cancel' :
317+ specific_request = CancelTaskRequest .model_validate (
318+ body
319+ )
320+ case 'tasks/pushNotificationConfig/set' :
321+ specific_request = (
322+ SetTaskPushNotificationConfigRequest .model_validate (
323+ body
324+ )
325+ )
326+ case 'tasks/pushNotificationConfig/get' :
327+ specific_request = (
328+ GetTaskPushNotificationConfigRequest .model_validate (
329+ body
330+ )
331+ )
332+ case 'tasks/pushNotificationConfig/list' :
333+ specific_request = ListTaskPushNotificationConfigRequest .model_validate (
334+ body
335+ )
336+ case 'tasks/pushNotificationConfig/delete' :
337+ specific_request = DeleteTaskPushNotificationConfigRequest .model_validate (
338+ body
339+ )
340+ case 'tasks/resubscribe' :
341+ specific_request = (
342+ TaskResubscriptionRequest .model_validate (body )
343+ )
344+ case 'agent/getAuthenticatedExtendedCard' :
345+ specific_request = (
346+ GetAuthenticatedExtendedCardRequest .model_validate (
347+ body
348+ )
349+ )
350+ case _:
351+ return self ._generate_error_response (
352+ request_id , A2AError (root = MethodNotFoundError ())
353+ )
354+ except ValidationError as e :
355+ traceback .print_exc ()
356+ return self ._generate_error_response (
357+ request_id ,
358+ A2AError (
359+ root = InvalidParamsError (data = json .loads (e .json ()))
360+ ),
361+ )
277362
363+ # 3) Build call context and wrap the request for downstream handling
278364 call_context = self ._context_builder .build (request )
279365
280- request_id = a2a_request .root .id
366+ request_id = specific_request .id
367+ a2a_request = A2ARequest (root = specific_request )
281368 request_obj = a2a_request .root
282369
283370 if isinstance (
@@ -301,12 +388,6 @@ async def _handle_requests(self, request: Request) -> Response: # noqa: PLR0911
301388 return self ._generate_error_response (
302389 None , A2AError (root = JSONParseError (message = str (e )))
303390 )
304- except ValidationError as e :
305- traceback .print_exc ()
306- return self ._generate_error_response (
307- request_id ,
308- A2AError (root = InvalidRequestError (data = json .loads (e .json ()))),
309- )
310391 except HTTPException as e :
311392 if e .status_code == HTTP_413_REQUEST_ENTITY_TOO_LARGE :
312393 return self ._generate_error_response (
0 commit comments