Using Status Codes for gPRC
People are often used to REST status codes and have experience using them (even if they are still often abused), but status codes in gRPC work a little bit differently and do not follow the same numeric range as REST status codes. Let’s see how they are used!
Below you will find a handy reference of each status code and when to use it. Furthermore there is an example of how to implement them in your Go server and to handle them in your Go client. To get the most out of this blog, you’ll need to have a beginner understanding of gRPC. You could get that by reading the official documentation or completing our course which will take you from no knowledge to expert.
gRPC Status Codes
Below is all of the status codes available in gRPC. I have also tried to compare it to a HTTP status code but as you will see, gRPC tends to be a little more granular and so there is not always a direct mapping.
OK (code: 0)
Return this when there is no error and the request was successful.
Similar to 200 HTTP Status OK
CANCELLED (code: 1)
The operation was canceled, usually by the client (or user) before completion.
Similar to HTTP 499 Client Closed Request (commonly used by some servers), although not an official status code.
UNKNOWN (code: 2)
An unknown error occurred. This can be returned when an error from another system returns an error you did not expect or another unspecified error happens.
Similar to HTTP 500 Internal Server Error.
INVALID_ARGUMENT (code: 3)
The client provided an invalid argument.
Similar to HTTP 400 Bad Request.
DEADLINE_EXCEEDED (code: 4)
The operation took longer than the allowed deadline to complete. Even if the operation succeeds eventually, the deadline has already passed.
Similar to HTTP 504 Gateway Timeout.
NOT_FOUND (code: 5)
A requested entity, such as a file or resource, could not be found.
Similar to HTTP 404 Not Found.
ALREADY_EXISTS (code: 6)
The entity that the client tried to create already exists.
Similar to HTTP 409 Conflict.
PERMISSION_DENIED (code: 7)
The client does not have sufficient permissions to perform the requested operation. The client is authenticated but lacks proper authorization.
Similar to HTTP 403 Forbidden.
RESOURCE_EXHAUSTED (code: 8)
Some resource has been depleted, such as quota limits being exceeded or a system running out of space.
Similar to HTTP 429 Too Many Requests
FAILED_PRECONDITION (code: 9)
The system state is not suitable for the requested operation. This error indicates that the operation cannot proceed due to the current system condition.
Similar to HTTP 412 Precondition Failed.
ABORTED (code: 10)
The operation was aborted due to concurrency issues, such as a conflict in a multi-operation transaction.
Similar to HTTP 409 Conflict.
OUT_OF_RANGE (code: 11)
The operation was attempted beyond the valid range, like reading past the end of a file.
Similar to HTTP 416 Range Not Satisfiable.
UNIMPLEMENTED (code: 12)
The operation is not supported or is not implemented by the service.
Similar to HTTP 501 Not Implemented.
INTERNAL (code: 13)
An internal error occurred, usually indicating a failure of the underlying system’s integrity or unexpected conditions.
Similar to HTTP 500 Internal Server Error.
UNAVAILABLE (code: 14)
The service is currently unavailable, which might be a temporary issue that could be resolved with retries.
Similar to HTTP 503 Service Unavailable.
DATA_LOSS (code: 15)
Significant data loss or corruption occurred, which cannot be recovered.
Similar to HTTP 500 Internal Server Error.
UNAUTHENTICATED (code: 16)
The request lacks valid authentication credentials.
Similar to HTTP 401 Unauthorized.
Handling Errors with status codes
If a gRPC call is successful, we return the ok
(code 0) response, but what about when the call is not? It is good practice to return one of the predefined error codes above, rather than defining your own. There is a great example of how to both return the errors and handle them in multiple languages here. Here’s what a minimal example looks like for Go:
import (
"context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
pb "example/hello"
)
type server struct {
pb.UnimplementedHelloService
}
func (s *server) SayHello(_ context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
if in.Name == "" {
return nil, status.Errorf(codes.InvalidArgument, "request missing required field: Name")
}
return &pb.HelloReply{Message: "Hi " + req.Name}, nil
}
As you can see, we can still use Go’s paradigms of wrapping an error to add more context, except we use the status package instead of the fmt package from the standard library.
The generated client would handle it something like this:
package main
import (
"context"
"flag"
"log"
"os/user"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials/insecure"
pb "example/hello"
"google.golang.org/grpc/status"
)
func main() {
conn, err := grpc.NewClient(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("Failed to connect to server: %v", err)
}
defer conn.Close()
c := pb.NewHelloClient(conn)
name := "Matt"
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
if err != nil {
if status.Code(err) != codes.InvalidArgument {
log.Printf("Received unexpected error: %v", err)
continue
}
log.Printf("Received invalid argument error: %v", err)
continue
}
log.Printf("Response: %s", r.Message)
}
If you expected multiple different errors, you could add a switch statement or multiple if statements to handle them.
You Now Know Everything You Need to Know About Status Codes and gRPC!
If you’re still a little unsure about gRPC itself, you can read about why I believe gRPC and Go are a match made in heaven, and you can dive really deep with our video course.