Errors
GRPCError
is a main error you should expect
on the client-side and raise occasionally on the server-side.
Error Details
There is a possibility to send and receive rich error details, which may
provide much more context than status and message alone. These details are
encoded using google.rpc.Status
message and sent with trailing metadata.
This message becomes available after optional package install:
$ pip3 install googleapis-common-protos
There are some already defined error details in the
google.rpc.error_details_pb2
module, but you’re not limited to them, you can
send any message you want.
Here is how to send these details from the server-side:
from google.rpc.error_details_pb2 import BadRequest
async def Method(self, stream):
...
raise GRPCError(
Status.INVALID_ARGUMENT,
'Request validation failed',
[
BadRequest(
field_violations=[
BadRequest.FieldViolation(
field='title',
description='This field is required',
),
],
),
],
)
Here is how to dig into every detail on the client-side:
from google.rpc.error_details_pb2 import BadRequest
try:
reply = await stub.Method(Request(...))
except GRPCError as err:
if err.details:
for detail in err.details:
if isinstance(detail, BadRequest):
for violation in detail.field_violations:
print(f'{violation.field}: {violation.description}')
Note
In order to automatically decode these messages (details), you have to import them, otherwise you will see such stubs in the list of error details:
Unknown('google.rpc.QuotaFailure')
Client-Side
Here is an example to illustrate how errors propagate from inside the grpclib methods back to the caller:
async with stub.SomeMethod.open() as stream:
await stream.send_message(Request(...))
reply = await stream.recv_message() # gRPC error received during this call
Exceptions are propagated this way:
CancelledError
is raised insiderecv_message()
coroutine to interrupt itrecv_message()
coroutine handles this error and raiseStreamTerminatedError
instead or other error when it is possible to explain why coroutine was cancelledwhen the
open()
context-manager exits, it may handle transitive errors such asStreamTerminatedError
and raise properGRPCError
instead when possible
So here is a rule of thumb: expect GRPCError
outside the open()
context-manager:
try:
async with stub.SomeMethod.open() as stream:
await stream.send_message(Request(...))
reply = await stream.recv_message()
except GRPCError as error:
print(error.status, error.message)
Server-Side
Here is an example to illustrate how request cancellation is performed:
class Greeter(GreeterBase):
async def SayHello(self, stream):
try:
...
await asyncio.sleep(1) # cancel happens here
...
finally:
pass # cleanup
Task running
SayHello
coroutine gets cancelled andCancelledError
is raised inside itYou can use try..finally clause and/or context managers to properly cleanup used resources
When
SayHello
coroutine finishes, grpclib server internally re-raisesCancelledError
asTimeoutError
orStreamTerminatedError
to explain why request was cancelledIf cancellation isn’t performed clearly, e.g.
SayHello
raises another exception instead ofCancelledError
, this error is logged.
Reference
- exception grpclib.exceptions.GRPCError(status: Status, message: Optional[str] = None, details: Optional[Any] = None)
Expected error, may be raised during RPC call
There can be multiple origins of this error. It can be generated on the server-side and on the client-side. If this error originates from the server, on the wire this error is represented as
grpc-status
andgrpc-message
trailers. Possible values of thegrpc-status
trailer are described in the gRPC protocol definition. Ingrpclib
these values are represented asStatus
enum.Here are possible origins of this error:
you may raise this error to cancel current call on the server-side or return non-OK
Status
usingsend_trailing_metadata()
method (e.g. resource not found)server may return non-OK
grpc-status
in different failure conditions (e.g. invalid request)client raises this error for non-OK
grpc-status
from the serverclient may raise this error in different failure conditions (e.g. server returned unsupported
:content-type
header)
- message
Error message
- details
Error details
- exception grpclib.exceptions.ProtocolError
Unexpected error, raised by
grpclib
when your code violates gRPC protocolThis error means that you probably should fix your code.
- exception grpclib.exceptions.StreamTerminatedError
Unexpected error, raised when we receive
RST_STREAM
frame from the other sideThis error means that the other side decided to forcefully cancel current call, probably because of a protocol error.
- class grpclib.const.Status(value)
Predefined gRPC status codes represented as enum
See also: https://github.com/grpc/grpc/blob/master/doc/statuscodes.md
- OK = 0
The operation completed successfully
- CANCELLED = 1
The operation was cancelled (typically by the caller)
- UNKNOWN = 2
Generic status to describe error when it can’t be described using other statuses
- INVALID_ARGUMENT = 3
Client specified an invalid argument
- DEADLINE_EXCEEDED = 4
Deadline expired before operation could complete
- NOT_FOUND = 5
Some requested entity was not found
- ALREADY_EXISTS = 6
Some entity that we attempted to create already exists
- PERMISSION_DENIED = 7
The caller does not have permission to execute the specified operation
- RESOURCE_EXHAUSTED = 8
Some resource has been exhausted, perhaps a per-user quota, or perhaps the entire file system is out of space
- FAILED_PRECONDITION = 9
Operation was rejected because the system is not in a state required for the operation’s execution
- ABORTED = 10
The operation was aborted
- OUT_OF_RANGE = 11
Operation was attempted past the valid range
- UNIMPLEMENTED = 12
Operation is not implemented or not supported/enabled in this service
- INTERNAL = 13
Internal errors
- UNAVAILABLE = 14
The service is currently unavailable
- DATA_LOSS = 15
Unrecoverable data loss or corruption
- UNAUTHENTICATED = 16
The request does not have valid authentication credentials for the operation