gRPC-GoMiddleTechnical
Как обрабатывать ошибки в gRPC-Go? Что такое gRPC status codes?
gRPC-Go использует status.Error(codes.NotFound, "msg") для возврата ошибок с сервера; на клиенте — status.FromError(err) для получения кода и сообщения. Обычный error без обёртки в status превращается в codes.Unknown.
Обработка ошибок в gRPC-Go: status codes
gRPC использует собственную систему кодов состояния вместо HTTP-статусов. Каждая ошибка содержит числовой код (codes.Code) и текстовое сообщение. В Go это реализовано через пакеты google.golang.org/grpc/codes и google.golang.org/grpc/status.
Ключевые коды
codes.OK(0) — успехcodes.Canceled(1) — вызов отменён клиентомcodes.Unknown(2) — неизвестная ошибка (возвращается если передать обычный error)codes.InvalidArgument(3) — неверные аргументы запросаcodes.NotFound(5) — ресурс не найденcodes.AlreadyExists(6) — конфликт при созданииcodes.PermissionDenied(7) — нет правcodes.ResourceExhausted(8) — rate limit / квотаcodes.FailedPrecondition(9) — нарушено предусловиеcodes.Unavailable(14) — сервис временно недоступен (безопасно для retry)codes.DeadlineExceeded(4) — истёк deadlinecodes.Unauthenticated(16) — нет или неверные credentials
Возврат ошибки с сервера
package main
import (
"context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
pb "example.com/proto/user"
)
type userServer struct {
pb.UnimplementedUserServiceServer
}
func (s *userServer) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.User, error) {
if req.Id == "" {
return nil, status.Error(codes.InvalidArgument, "user id is required")
}
user, err := db.FindUser(req.Id)
if err == ErrNotFound {
return nil, status.Errorf(codes.NotFound, "user %q not found", req.Id)
}
if err != nil {
return nil, status.Error(codes.Internal, "database error")
}
return user, nil
}
Получение и разбор ошибки на клиенте
resp, err := client.GetUser(ctx, &pb.GetUserRequest{Id: userID})
if err != nil {
st, ok := status.FromError(err)
if !ok {
// не gRPC ошибка
log.Printf("non-gRPC error: %v", err)
return
}
switch st.Code() {
case codes.NotFound:
log.Printf("user not found: %s", st.Message())
case codes.InvalidArgument:
log.Printf("bad request: %s", st.Message())
case codes.Unavailable:
// можно повторить запрос
log.Printf("service unavailable, retry later")
default:
log.Printf("unexpected error %v: %s", st.Code(), st.Message())
}
return
}
Детальные ошибки через proto (google.rpc.Status)
Пакет google.golang.org/genproto/googleapis/rpc/errdetails позволяет прикреплять структурированные детали к ошибке:
import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/genproto/googleapis/rpc/errdetails"
)
st, _ := status.New(codes.InvalidArgument, "validation failed").
WithDetails(&errdetails.BadRequest{
FieldViolations: []*errdetails.BadRequest_FieldViolation{
{Field: "email", Description: "must be a valid email address"},
},
})
return nil, st.Err()
Подводные камни
- Возврат обычного error — если вернуть
fmt.Errorf(...)вместоstatus.Error, клиент получитcodes.Unknownи потеряет семантику ошибки. - status.FromError для non-gRPC errors —
okбудетfalse, ноstвсё равно вернётcodes.Unknown; важно проверять флаг. - Утечка внутренних деталей — не передавайте в message строки SQL-запросов или stack trace; используйте
codes.Internalс общим сообщением и логируйте подробности отдельно. - codes.Canceled vs DeadlineExceeded — Canceled возвращается когда клиент отменил контекст, DeadlineExceeded — когда истёк таймаут; смешивать их опасно при решении о retry.
- Retry только на безопасных кодах — повторять запрос безопасно только при
UnavailableиResourceExhausted; повтор приInternalможет привести к дублированию операций. - Потеря деталей при обёртке —
fmt.Errorf("wrap: %w", grpcErr)уничтожает gRPC-статус; используйтеstatus.FromErrorдо обёртки. - Игнорирование контекста — не проверять
ctx.Err()в handler-е и возвращатьcodes.Internalвместоcodes.Canceledпри отмене ведёт к некорректному мониторингу.
Common mistakes
- Давать ответ про ошибки и status codes в gRPC-Go только на уровне определения, не показывая поведение в реальном приложении.
- Игнорировать границы ответственности вокруг темы «ошибки и status codes в gRPC-Go»: кто отменяет работу, кто владеет ресурсом и где формируется ответ клиенту.
- Не связывать ошибки и status codes в gRPC-Go с observability, тестированием или безопасностью, когда это влияет на продакшен-поведение.
What the interviewer is testing
- Точно объясняет, что именно делает ошибки и status codes в gRPC-Go и где это используется в Go-коде.
- Связывает ошибки и status codes в gRPC-Go с корректным lifecycle запроса, отменой, конкурентностью или конфигурацией сервера там, где это уместно.
- Не изобретает API и опирается на реальные контракты официальной документации.