Chapter 13: Status Codes and Error Handling
gRPC Status Codes
| Code | Number | Meaning | When to Use |
|---|---|---|---|
| OK | 0 | Success | RPC completed successfully |
| CANCELLED | 1 | Client cancelled | Client called cancel |
| INVALID_ARGUMENT | 3 | Bad request | Validation failed (like HTTP 400) |
| NOT_FOUND | 5 | Resource not found | Entity doesn't exist (like HTTP 404) |
| ALREADY_EXISTS | 6 | Conflict | Duplicate creation (like HTTP 409) |
| PERMISSION_DENIED | 7 | Forbidden | Authenticated but not authorized |
| UNAUTHENTICATED | 16 | No credentials | Missing or invalid auth token |
| RESOURCE_EXHAUSTED | 8 | Rate limited | Quota exceeded (like HTTP 429) |
| UNAVAILABLE | 14 | Service down | Transient failure, retry (like HTTP 503) |
| INTERNAL | 13 | Server bug | Unexpected error (like HTTP 500) |
| DEADLINE_EXCEEDED | 4 | Timeout | RPC took too long |
| UNIMPLEMENTED | 12 | Not implemented | Method not supported |
Returning Errors (Server)
grpc::Status GetUser(ServerContext* ctx, const GetUserRequest* req, User* resp) {
if (req->user_id().empty()) {
return grpc::Status(grpc::INVALID_ARGUMENT, "user_id is required");
}
auto user = db_->Find(req->user_id());
if (!user) {
return grpc::Status(grpc::NOT_FOUND, "user not found");
}
*resp = *user;
return grpc::Status::OK;
}
Handling Errors (Client)
grpc::Status status = stub->GetUser(&ctx, req, &resp);
if (!status.ok()) {
switch (status.error_code()) {
case grpc::NOT_FOUND:
// handle missing user
break;
case grpc::UNAVAILABLE:
// retry with backoff
break;
default:
LOG_ERROR("RPC failed: %s", status.error_message().c_str());
}
}
💡 Retryable vs Non-Retryable
UNAVAILABLE and DEADLINE_EXCEEDED are retryable. INVALID_ARGUMENT and NOT_FOUND are not (retrying won't help). INTERNAL might be retryable depending on the cause.