120120 BackPressureError ,
121121 DeploymentUnavailableError ,
122122 RayServeException ,
123+ gRPCStatusError ,
123124)
124125from ray .serve .generated .serve_pb2 import (
125126 ASGIRequest ,
@@ -972,9 +973,13 @@ async def handle_request(
972973 )
973974 with self ._wrap_request (request_metadata , ray_trace_ctx ):
974975 async with self ._start_request (request_metadata ):
975- return await self ._user_callable_wrapper .call_user_method (
976- request_metadata , request_args , request_kwargs
977- )
976+ try :
977+ return await self ._user_callable_wrapper .call_user_method (
978+ request_metadata , request_args , request_kwargs
979+ )
980+ except Exception as e :
981+ # For gRPC requests, wrap exception with user-set status code
982+ raise self ._maybe_wrap_grpc_exception (e , request_metadata ) from e
978983
979984 async def handle_request_streaming (
980985 self , request_metadata : RequestMetadata , * request_args , ** request_kwargs
@@ -987,22 +992,45 @@ async def handle_request_streaming(
987992 request_metadata , ray_trace_ctx
988993 ) as status_code_callback :
989994 async with self ._start_request (request_metadata ):
990- if request_metadata .is_http_request :
991- scope , receive = request_args
992- async for msgs in self ._user_callable_wrapper .call_http_entrypoint (
993- request_metadata ,
994- status_code_callback ,
995- scope ,
996- receive ,
997- ):
998- yield pickle .dumps (msgs )
999- else :
1000- async for result in self ._user_callable_wrapper .call_user_generator (
1001- request_metadata ,
1002- request_args ,
1003- request_kwargs ,
1004- ):
1005- yield result
995+ try :
996+ if request_metadata .is_http_request :
997+ scope , receive = request_args
998+ async for msgs in self ._user_callable_wrapper .call_http_entrypoint (
999+ request_metadata ,
1000+ status_code_callback ,
1001+ scope ,
1002+ receive ,
1003+ ):
1004+ yield pickle .dumps (msgs )
1005+ else :
1006+ async for result in self ._user_callable_wrapper .call_user_generator (
1007+ request_metadata ,
1008+ request_args ,
1009+ request_kwargs ,
1010+ ):
1011+ yield result
1012+ except Exception as e :
1013+ # For gRPC requests, wrap exception with user-set status code
1014+ raise self ._maybe_wrap_grpc_exception (e , request_metadata ) from e
1015+
1016+ def _maybe_wrap_grpc_exception (
1017+ self , e : BaseException , request_metadata : RequestMetadata
1018+ ) -> BaseException :
1019+ """Wrap exception with gRPCStatusError if user set a status code.
1020+
1021+ For gRPC requests, if the user set a status code on the grpc_context before
1022+ raising an exception, we wrap the exception with gRPCStatusError to preserve
1023+ the user's intended status code through the error handling path.
1024+ """
1025+ if request_metadata .is_grpc_request :
1026+ grpc_context = request_metadata .grpc_context
1027+ if grpc_context and grpc_context .code ():
1028+ return gRPCStatusError (
1029+ original_exception = e ,
1030+ code = grpc_context .code (),
1031+ details = grpc_context .details (),
1032+ )
1033+ return e
10061034
10071035 async def handle_request_with_rejection (
10081036 self , request_metadata : RequestMetadata , * request_args , ** request_kwargs
@@ -1032,26 +1060,30 @@ async def handle_request_with_rejection(
10321060 num_ongoing_requests = self .get_num_ongoing_requests (),
10331061 )
10341062
1035- if request_metadata .is_http_request :
1036- scope , receive = request_args
1037- async for msgs in self ._user_callable_wrapper .call_http_entrypoint (
1038- request_metadata ,
1039- status_code_callback ,
1040- scope ,
1041- receive ,
1042- ):
1043- yield pickle .dumps (msgs )
1044- elif request_metadata .is_streaming :
1045- async for result in self ._user_callable_wrapper .call_user_generator (
1046- request_metadata ,
1047- request_args ,
1048- request_kwargs ,
1049- ):
1050- yield result
1051- else :
1052- yield await self ._user_callable_wrapper .call_user_method (
1053- request_metadata , request_args , request_kwargs
1054- )
1063+ try :
1064+ if request_metadata .is_http_request :
1065+ scope , receive = request_args
1066+ async for msgs in self ._user_callable_wrapper .call_http_entrypoint (
1067+ request_metadata ,
1068+ status_code_callback ,
1069+ scope ,
1070+ receive ,
1071+ ):
1072+ yield pickle .dumps (msgs )
1073+ elif request_metadata .is_streaming :
1074+ async for result in self ._user_callable_wrapper .call_user_generator (
1075+ request_metadata ,
1076+ request_args ,
1077+ request_kwargs ,
1078+ ):
1079+ yield result
1080+ else :
1081+ yield await self ._user_callable_wrapper .call_user_method (
1082+ request_metadata , request_args , request_kwargs
1083+ )
1084+ except Exception as e :
1085+ # For gRPC requests, wrap exception with user-set status code
1086+ raise self ._maybe_wrap_grpc_exception (e , request_metadata ) from e
10551087
10561088 @abstractmethod
10571089 async def _on_initialized (self ):
0 commit comments