Chapter 13: Status Codes and Error Handling

gRPC Status Codes

CodeNumberMeaningWhen to Use
OK0SuccessRPC completed successfully
CANCELLED1Client cancelledClient called cancel
INVALID_ARGUMENT3Bad requestValidation failed (like HTTP 400)
NOT_FOUND5Resource not foundEntity doesn't exist (like HTTP 404)
ALREADY_EXISTS6ConflictDuplicate creation (like HTTP 409)
PERMISSION_DENIED7ForbiddenAuthenticated but not authorized
UNAUTHENTICATED16No credentialsMissing or invalid auth token
RESOURCE_EXHAUSTED8Rate limitedQuota exceeded (like HTTP 429)
UNAVAILABLE14Service downTransient failure, retry (like HTTP 503)
INTERNAL13Server bugUnexpected error (like HTTP 500)
DEADLINE_EXCEEDED4TimeoutRPC took too long
UNIMPLEMENTED12Not implementedMethod 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.