View on GitHub

gRPC Errors

A handy guide to gRPC errors

gRPC is an amazing library, however, the documentation lacks details on error handling. The code examples do not contain error handling part either (though few does seem to have them). Some test suits in main repo do have them, but it might be difficult to figure out for a beginner.

This project is my attempt to fix the issue. The repository contains examples in all popular languages and contain similar implementations of server and client across languages and also, error handling. So you can run server in one language of your choice and client in another, error handling still works seamlessly. The rest of this page shows how to send errors from server, handle them in client, in different languages.

C++

Check the complete example here.

Server

To send an error, return an instance of grpc::Status with error message and code:

Status(<grpc error code>, <error message>);

example:

Status(StatusCode::INVALID_ARGUMENT, "Ouch!");

Client

To handle the error, check error_code and error_message members of grpc::Status:

Status status = stub_->GRPCMethod(&context, request, &response);
status.error_message();
status.error_code();

C#

Check the complete example here.

Server

To send an error, raise RpcException with error message and code:

RpcException(new Status(<grpc error code>, <error message>));

Example:

throw new RpcException(new Status(StatusCode.InvalidArgument, "Ouch!"));

Client

To handle the error, catch RpcException and access its member Status:

catch (RpcException e){
    e.Status.Detail;
    e.Status.StatusCode;
}

Go

Check the complete example here.

Server

To send an error, return status.Errorf with error message and code:

status.Errorf(<grpc error code>, <error message>)

Example:

return status.Errorf(codes.InvalidArgument, "Ouch!")

Client

To handle the error, check error returned from gRPC call:

_, err := client.GRPCMethod(...)
statusCode := status.FromError(err)

Node

Check the complete example here.

Server

To send an error, return with code and message:

return callback({
    code: <grpc error code>,
    message: <error message>,
});

Example:

return callback({
    code: grpc.status.INVALID_ARGUMENT,
    message: "Ouch!",
});

Client

To handle the error, check error returned from gRPC call:

client.grpcMethod({...}, function(err, response) {
    err.message;
    err.code;
});

Objective C

Check the complete example here.

Client

To handle the error, check error returned from gRPC call:

[client grpcMethod:request handler:^(..., NSError *error) {
    error.localizedDescription;
    error.code;
}];

Python

Check the complete example here.

Server

To send an error, set code and details for context object:

context.set_code(<grpc error code>)
context.set_details(<error message>)

Example:

def GRPCMethod(self, request, context):
    context.set_details("Ouch!")
    context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
    return response

Client

To handle the error, catch grpc.RpcError exception:

try:
    ...
except grpc.RpcError as e:
    e.details()
    status_code = e.code()
    status_code.name
    status_code.value

Ruby

Check the complete example here.

Server

To send an error, raise GRPC::BadStatus exception with code and message details:

GRPC::BadStatus.new(<grpc error code>, <error message>)

Example:

raise GRPC::BadStatus.new(GRPC::Core::StatusCodes::INVALID_ARGUMENT, "Ouch!")

Client

To handle the error, catch GRPC::BadStatus exception:

begin
  ...
rescue GRPC::BadStatus => e
  e.details
  e.code
end

Swift

Check the complete example here.

Client

To handle the error, check error returned from gRPC call:

client.grpcMethod(request, handler: { (..., error: NSError?) in
    error.localizedDescription;
    error.code;
})

Scala

Check the complete example here. The example assumes you are using ScalaPB which uses grpc-java under the hood but has various conveniences suitable for programming in Scala.

Server

If in a unary call, you simply use the regular Future constructs to denote success and failure. In the case of failures, the best practice is to use the static methods on the Status class from the io.grpc package and convert that to a io.grpc.StatusRuntimeException. According to the documentation, gRPC stubs prefer StatusRuntimeException to StatusException.

Status
    .<grpc error code>
    .augmentDescription(<error message>)
    .asRuntimeException()

Example:

def GRPCMethod(request: Request): Future[Response] = {
Future.failed(
    Status
        .<grpc error code>
        .augmentDescription(<error message>)
        .asRuntimeException()
    )
}

Client

To handle the error, if using a blocking stub, you can use any of Scala’s standard mechanisms to catch the Throwable. Be careful because StatusException and StatusRuntimeException are Java classes that do not have a common ancestor other than Exception. If using the async stub again use the standard constructs provided by Scala Futures.

client.grpcMethod(...) onFailure {
    case ex: StatusRuntimeException =>
        val desc = ex.getStatus.getDescription
        val code = ex.getStatus.getCode
}

Rust

Check the complete example here. The example assumes we are using grpcio.

Server

grpcio uses futures::sink::Sink to send values asynchronously.

To send an error simply call the fail method on the sink with an instance of grpcio::RpcStatus.

Example:

let future = sink
    .fail(RpcStatus::new(
        RpcStatusCode::InvalidArgument,
        Some("Ouch!".to_owned()),
    ));

Client

To handle the error, check the Result returned by the method call for an grpcio::Error.

let reply = client.GRPCMethod(&request);

match reply {
    Err(Error::RpcFailure(e)) => {
        assert_eq!(e.status, RpcStatusCode::InvalidArgument);
        assert_eq!(e.details, Some("Ouch!".to_owned()));
    },
    Ok(reply) => {
        // ...
    }
}