In gRPC, we should never return error in success response. It should be like either we return the successResponse or we return the errorResponse. If we are return for example errorResponse then the successResponse will be nil
.
Golang has a package called status
which is widely used for error handling. It has an internal function Error()
which converts it to error
object which is returned.
// status.proto
message Status {
// The status code, which should be an enum value of [google.rpc.Code]
int32 code = 1;
// error-message
string message = 2;
// A list of messages that carry the error details.
repeated google.protobuf.Any details = 3;
}
So, We should send grpc error code in code
field. We can send a general message related to error occured or ErrorCodes like "VALIDATION_ERROR", "AUTH_ERROR", etc. in message
field. And, the additional information or stack trace or developer error message should be sent in details
field.
Example 1:
err := status.Errorf(codes.InvalidArgument, "First Name is required")
Example 2:
//error.proto
message ErrorDetails {
string code = 1;
string additional_info = 2;
}
err := status.Errorf(codes.InvalidArgument, "First Name is required")
err, _ := err.WithDetails(&ErrorDetails{
Code: "VALIDATION_ERROR",
AdditionalInfo: "Error occured at third-party API"
})
We can also create our own custom struct similar to Status which should implement 2 methods - Error()
and GRPCStatus()
and gRPC will take care of rest.
Note: In case, we are using grpc-gateway and serving both grpc and http servers via proxy. In that scenerio, we can create a custom interceptor using WithErrorHandler
to covert status
object to our more client-friendly error object.