Sending response with non-ok status is not allowed for unary-unary RPC (non-streaming on both sides). For streaming RPC, the server may send response before sending the error code, but is not recommended. Mixing normal response with error status could result in future maintainability issues, e.g. if the same error apply to multiple RPC, should all the response ProtoBuf message include those fields?
Back to your question, the "captcha-token" should be considered a part of the error status, hence it can be added as one of the trailing metadata. In your case, you may add the serialized proto message as a binary trailing metadata by adding a -bin
suffix in your trailing metadata key.
Also, there is an official supported package grpcio-status
that does this for you.
The server side packs rich error status into a "grpc_status.status_pb2.Status" proto message. The example below just uses the common error protos, but you may pack "any" proto into details
, as long as your client understands them.
# Server side
from grpc_status import rpc_status
from google.protobuf import any_pb2
def ...Servicer(...):
def AnRPCCall(request, context):
...
detail = any_pb2.Any()
detail.Pack(
rpc_status.error_details_pb2.DebugInfo(
stack_entries=traceback.format_stack(),
detail="Can't recognize this argument",
)
)
rich_status = grpc_status.status_pb2.Status(
code=grpc_status.code_pb2.INVALID_ARGUMENT,
message='Invalid argument',
details=[detail]
)
context.abort_with_status(rpc_status.to_status(rich_status))
# The method handler will abort
The client side decodes the error, and reacts on them.
# Client side
try:
self._channel.unary_unary(_ERROR_DETAILS).with_call(_REQUEST)
except grpc.RpcError as rpc_error:
status = rpc_status.from_call(rpc_error)
for detail in status.details:
if detail.Is(error_details_pb2.DebugInfo.DESCRIPTOR):
info = error_details_pb2.DebugInfo()
detail.Unpack(info)
# Handle the debug info
elif detail.Is(OtherErrorProto.DESCRIPTOR):
# Handle the other error proto
else:
# Handle unknown error
Learn more about the rich status: https://github.com/grpc/proposal/blob/master/L44-python-rich-status.md