Códigos de Error y Mapeo de Estado gRPC
Los errores cruzan los límites del proceso como códigos de estado gRPC, no como cadenas de error de Go.
Busca en todas las páginas de la documentación
Los errores cruzan los límites del proceso como códigos de estado gRPC, no como cadenas de error de Go.
Los clientes deben inspeccionar los códigos; los servidores deben mapear las fallas del dominio deliberadamente.
google.golang.org/grpc/status envuelve un codes.Code, un mensaje y protobufs de detalle opcionales.
Los servidores devuelven status.Error o status.Errorf; los clientes desempaquetan con status.FromError.
Dieciséis códigos estándar cubren la mayoría de los escenarios de sistemas distribuidos.
Los detalles estructurados (google.rpc.ErrorInfo, protos personalizados) permiten que las interfaces de usuario y las reintentos actúen sobre campos legibles por máquina.
Tratar los errores gRPC como cadenas opacas de fmt.Errorf pierde información en cada salto.
Tarjeta de receta de referencia rápida - lista para copiar y pegar.
import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func findUser(id string) error {
if id == "" {
return status.Error(codes.InvalidArgument, "se requiere id")
}
if !exists(id) {
return status.Error(codes.NotFound, "usuario no encontrado")
}
return nil
}// Cliente
st, ok := status.FromError(err)
if ok && st.Code() == codes.NotFound {
// manejar recurso faltante
}Cuándo usar esto:
InvalidArgument) frente a entidades faltantes (NotFound).ResourceExhausted) o mantenimiento (Unavailable) para la lógica de reintento.ErrorInfo con reason y 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, "la cantidad debe ser positiva")
br := &errdetails.BadRequest{
FieldViolations: []*errdetails.BadRequest_FieldViolation{
{Field: "qty", Description: "debe ser > 0"},
},
}
st, err := st.WithDetails(br)
if err != nil {
return status.Error(codes.Internal, "falló la adjunción de detalles")
}
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, "no hay suficiente stock").WithDetails(info)
return st.Err()
}
return nil
}
func clientHandle(err error) {
st, ok := status.FromError(err)
if !ok {
log.Fatal(err)
}
fmt.Println("código:", st.Code(), "msg:", st.Message())
for _, d := range st.Details() {
switch info := d.(type) {
case *errdetails.ErrorInfo:
fmt.Println("razón:", 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("tipo de detalle desconocido:", any.GetTypeUrl())
}
}
}
}
func inStock(sku string, qty int32) bool { return false }
func main() {
srv := &inventoryServer{}
err := srv.Reserve(context.Background(), "ABC", 0)
clientHandle(err)
}Lo que esto demuestra:
status.New más WithDetails adjunta mensajes de detalle estándar.st.Details() con cambios de tipo.FailedPrecondition señala violaciones de reglas de negocio distintas de NotFound.BadRequest familiares para los clientes JSON de grpc-gateway.En caso de fallo, gRPC envía un protobuf de estado en los trailers (cabeceras HTTP/2).
Go lo expone como un error que satisface status.FromError.
El envoltorio con %w preserva el estado si la cadena de envoltorio aún expone un estado gRPC.
Un simple errors.New se convierte en codes.Unknown a menos que se convierta.
| Código | Significado | Análogo HTTP (gateway) | ¿Reintentar? |
|---|---|---|---|
OK | Éxito | 200 | - |
InvalidArgument | Entrada incorrecta | 400 | No |
NotFound | Recurso faltante | 404 | No |
AlreadyExists | Creación duplicada | 409 | No |
PermissionDenied | Fallo de autorización | 403 | No |
Unauthenticated | Fallo de autenticación | 401 | No |
ResourceExhausted | Límite de tasa / cuota | 429 | Retroceder |
Unavailable | Interrupción transitoria | 503 | Sí, con jitter |
DeadlineExceeded | Tiempo de espera agotado | 504 | Quizás |
Canceled | Cliente cancelado | 499 | No |
Internal | Error del servidor | 500 | No |
// Preservar el estado a través de capas 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 errores de validación predecibles; los clientes no pueden remediarlos.status.Convert al conectar APIs heredadas.status.Code(err).Internal. Solución: interceptor de recuperación más mapeo explícito de estado.Unknown - Tormentas de reintentos y alertas incorrectas. Solución: elige el código estándar más cercano.google.rpc en la configuración del gateway.context.Canceled vs codes.Canceled - La desconexión del cliente puede aparecer de manera diferente. Solución: normalizar en las bibliotecas del cliente.| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| Estado gRPC + detalles | Todos los servicios gRPC | - |
Devolver solo error dentro del proceso | Paquetes privados que nunca cruzan RPC | Límites de servicio públicos |
problem+json HTTP en el borde | BFF solo para navegador | RPC interno de protobuf |
| Enums de resultado en la respuesta | Los resultados de negocio son ramas esperadas | Fallos verdaderos (autenticación, errores) |
Devuelve valores status.Error como error.
Los llamadores usan la misma interfaz error en todas partes.
Registra errores completos en el lado del servidor.
Devuelve mensajes genéricos Internal a los clientes a menos que los detalles seguros sean intencionales.
FailedPrecondition significa no reintentar hasta que el estado del sistema cambie.
Aborted sugiere que el reintento puede tener éxito inmediatamente (conflicto de concurrencia).
Sí, empaqueta con anypb.New y registra los tipos que los clientes entienden.
Las reglas predeterminadas mapean los códigos gRPC a estados HTTP.
Anula en las anotaciones google.api.http cuando sea necesario.
Usa status.FromError primero.
Los errores de estado envueltos pueden necesitar lógica Is personalizada.
Un InvalidArgument con múltiples violaciones de campo BadRequest es idiomático.
No, alerta y muestra el fallo.
Reintenta Unavailable con retroceso exponencial limitado.
A menudo como el error devuelto por la última llamada Recv o Send.
Los trailers transportan el estado después del cierre parcial del stream.
Los errores de estado participan en el envoltorio.
Los paquetes de dominio pueden definir errores centinela que se convierten en el límite de transporte.
Versiones de Stack: Esta página fue escrita para Go 1.26.x (GC por defecto Green Tea, go fix modernizers - verificar parche en la compilación), chi (última versión - verificar en la compilación), gin (última versión - verificar en la compilación), echo (última versión - verificar en la compilación), google.golang.org/grpc (última versión - verificar en la compilación), sigs.k8s.io/controller-runtime (última versión - verificar en la compilación), kubebuilder (última versión - verificar en la compilación), tinygo (última versión - verificar objetivos de placa en la compilación), wazero (última versión - verificar en la compilación), y golangci-lint (última versión - verificar conjunto de linters en la compilación).
Revisado por Chris St. John·Última actualización: 18 jul 2026