Códigos de Erro gRPC e Mapeamento de Status
Erros cruzam limites de processo como códigos de status gRPC, não strings de erro Go.
Busque em todas as páginas da documentação
Erros cruzam limites de processo como códigos de status gRPC, não strings de erro Go.
Clientes devem inspecionar códigos; servidores devem mapear falhas de domínio deliberadamente.
google.golang.org/grpc/status envolve um codes.Code, mensagem e protobufs de detalhe opcionais.
Servidores retornam status.Error ou status.Errorf; clientes desempacotam com status.FromError.
Dezesseis códigos padrão cobrem a maioria dos cenários de sistemas distribuídos.
Detalhes estruturados (google.rpc.ErrorInfo, protos personalizados) permitem que UIs e retentativas ajam sobre campos legíveis por máquina.
Tratar erros gRPC como cadeias opacas de fmt.Errorf perde informações em cada salto.
Cartão de receita de referência rápida - pronto para copiar e colar.
import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func findUser(id string) error {
if id == "" {
return status.Error(codes.InvalidArgument, "id required")
}
if !exists(id) {
return status.Error(codes.NotFound, "user not found")
}
return nil
}// Cliente
st, ok := status.FromError(err)
if ok && st.Code() == codes.NotFound {
// lida com recurso ausente
}Quando usar isso:
InvalidArgument) versus entidades ausentes (NotFound).ResourceExhausted) ou manutenção (Unavailable) para lógica de retentativa.ErrorInfo com reason e domain para gateways de API.package main
import (
"context"
"fmt"
"log"
"google.golang.org/genproto/googleapis/rpc/errdetails"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/anypb"
)
type inventoryServer struct{}
func (s *inventoryServer) Reserve(ctx context.Context, sku string, qty int32) error {
if qty <= 0 {
st := status.New(codes.InvalidArgument, "quantity must be positive")
br := &errdetails.BadRequest{
FieldViolations: []*errdetails.BadRequest_FieldViolation{
{Field: "qty", Description: "must be > 0"},
},
}
st, err := st.WithDetails(br)
if err != nil {
return status.Error(codes.Internal, "detail attach failed")
}
return st.Err()
}
if !inStock(sku, qty) {
info := &errdetails.ErrorInfo{
Reason: "INSUFFICIENT_STOCK",
Domain: "inventory.example.com",
Metadata: map[string]string{"sku": sku},
}
st, _ := status.New(codes.FailedPrecondition, "not enough stock").WithDetails(info)
return st.Err()
}
return nil
}
func clientHandle(err error) {
st, ok := status.FromError(err)
if !ok {
log.Fatal(err)
}
fmt.Println("code:", st.Code(), "msg:", st.Message())
for _, d := range st.Details() {
switch info := d.(type) {
case *errdetails.ErrorInfo:
fmt.Println("reason:", info.GetReason())
case *errdetails.BadRequest:
for _, v := range info.GetFieldViolations() {
fmt.Println(v.GetField(), v.GetDescription())
}
default:
if any, ok := d.(*anypb.Any); ok {
fmt.Println("unknown detail type:", any.GetTypeUrl())
}
}
}
}
func inStock(sku string, qty int32) bool { return false }
func main() {
srv := &inventoryServer{}
err := srv.Reserve(context.Background(), "ABC", 0)
clientHandle(err)
}O que isso demonstra:
status.New mais WithDetails anexa mensagens de detalhe padrão.st.Details() com switch de tipo.FailedPrecondition sinaliza violações de regras de negócios distintas de NotFound.BadRequest familiares aos clientes JSON grpc-gateway.Em caso de falha, o gRPC envia um protobuf de status nos trailers (cabeçalhos HTTP/2).
Go o expõe como error que satisfaz status.FromError.
Envolver com %w preserva o status se a cadeia de envolvimento ainda expuser um status gRPC.
errors.New simples se torna codes.Unknown a menos que seja convertido.
| Código | Significado | Análogo HTTP (gateway) | Retentar? |
|---|---|---|---|
OK | Sucesso | 200 | - |
InvalidArgument | Entrada inválida | 400 | Não |
NotFound | Recurso ausente | 404 | Não |
AlreadyExists | Criação duplicada | 409 | Não |
PermissionDenied | Falha de autorização | 403 | Não |
Unauthenticated | Falha de autenticação | 401 | Não |
ResourceExhausted | Limite de taxa / cota | 429 | Backoff |
Unavailable | Interrupção transitória | 503 | Sim, com jitter |
DeadlineExceeded | Tempo limite | 504 | Talvez |
Canceled | Cliente cancelou | 499 | Não |
Internal | Bug no servidor | 500 | Não |
// Preserva o status através de camadas auxiliares
func wrap(err error, msg string) error {
if err == nil {
return nil
}
if _, ok := status.FromError(err); ok {
return fmt.Errorf("%s: %w", msg, err)
}
return status.Errorf(codes.Internal, "%s: %v", msg, err)
}codes.Internal para erros de validação previsíveis - clientes não podem remediar.status.Convert ao interligar APIs legadas.status.Code(err).Internal. Correção: interceptor de recuperação mais mapeamento explícito de status.Unknown - Tempestades de retentativas e alertas ruins. Correção: escolha o código padrão mais próximo.google.rpc na configuração do gateway.context.Canceled vs codes.Canceled - Desconexão do cliente pode aparecer de forma diferente. Correção: normalize em bibliotecas de cliente.| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| Status gRPC + detalhes | Todos os serviços gRPC | - |
Retorno error apenas no processo | Pacotes privados que nunca cruzam RPC | Limites de serviço públicos |
| HTTP problem+json na borda | BFF apenas para navegador | RPC protobuf interno |
| Enums de resultado na resposta | Resultados de negócios são ramos esperados | Falhas reais (autenticação, bugs) |
Retorne valores status.Error como error.
Chamadores usam a mesma interface error em todos os lugares.
Registre erros completos no lado do servidor.
Retorne mensagens genéricas Internal para clientes, a menos que detalhes seguros sejam intencionais.
FailedPrecondition significa não retentar até que o estado do sistema mude.
Aborted sugere que a retentativa pode ter sucesso imediatamente (conflito de concorrência).
Sim - empacote com anypb.New e registre tipos que os clientes entendam.
Regras padrão mapeiam códigos gRPC para status HTTP.
Substitua em anotações google.api.http quando necessário.
Use status.FromError primeiro.
Erros de status envolvidos podem precisar de lógica Is personalizada.
Um InvalidArgument com múltiplas violações de campo BadRequest é idiomático.
Não - alerte e exponha a falha.
Retente Unavailable com backoff exponencial limitado.
Frequentemente como o retorno de erro da última chamada Recv ou Send.
Trailers carregam o status após o meio fechamento do stream.
Erros de status participam do envolvimento.
Pacotes de domínio podem definir erros sentinela convertidos na fronteira de transporte.
Versões de Stack: Esta página foi escrita para Go 1.26.x (padrão GC Green Tea, go fix modernizers - verifique o patch na compilação), chi (mais recente - verifique na compilação), gin (mais recente - verifique na compilação), echo (mais recente - verifique na compilação), google.golang.org/grpc (mais recente - verifique na compilação), sigs.k8s.io/controller-runtime (mais recente - verifique na compilação), kubebuilder (mais recente - verifique na compilação), tinygo (mais recente - verifique os alvos de placa na compilação), wazero (mais recente - verifique na compilação) e golangci-lint (mais recente - verifique o conjunto de linters na compilação).
Revisado por Chris St. John·Última atualização: 18 de jul. de 2026