Tipos de Error Personalizados e Interfaces de Error
Los tipos de error personalizados adjuntan datos estructurados a los fallos: pistas de estado HTTP, nombres de campo, indicadores de reintento y códigos de error opacos.
Busca en todas las páginas de la documentación
Los tipos de error personalizados adjuntan datos estructurados a los fallos: pistas de estado HTTP, nombres de campo, indicadores de reintento y códigos de error opacos.
Díseñalos para que los llamadores usen errors.As sin importar tus internos, y para que puedas añadir campos más tarde sin romper el contrato de error.
La interfaz error solo requiere Error() string.
Todo lo que va más allá es convención: tipos exportados, interfaces de comportamiento opcionales y envolturas no exportadas que mantienen tu implementación flexible.
Los errores enriquecidos potencian el mapeo de API y la observabilidad; las jerarquías sobre-diseñadas perjudican la simplicidad que Go favorece.
Tarjeta de receta de referencia rápida - lista para copiar y pegar.
type APIError struct {
Code string
Status int
Retry bool
}
func (e APIError) Error() string {
return e.Code
}
var err error = APIError{Code: "USER_MISSING", Status: 404}
var api APIError
if errors.As(err, &api) {
_ = api.Status
}Cuándo usar esto:
Code estables sin analizar cadenasTimeout() bool para reintentospackage main
import (
"errors"
"fmt"
)
type ValidationError struct {
Field string
Message string
}
func (e ValidationError) Error() string {
return fmt.Sprintf("validation: %s %s", e.Field, e.Message)
}
func (e ValidationError) HTTPStatus() int { return 422 }
func validateEmail(email string) error {
if email == "" {
return ValidationError{Field: "email", Message: "required"}
}
return nil
}
type statusCoder interface {
HTTPStatus() int
}
func writeResponse(err error) {
if err == nil {
fmt.Println("200 ok")
return
}
var sc statusCoder
if errors.As(err, &sc) {
fmt.Printf("%d %v\n", sc.HTTPStatus(), err)
return
}
fmt.Printf("500 %v\n", err)
}
func main() {
writeResponse(validateEmail(""))
}Lo que esto demuestra:
ValidationError es un tipo de valor con campos y una cadena Error() estableHTTPStatus() permite a los manejadores mapear sin un type switch en cada varianteerrors.As extrae el tipo concreto de cadenas envueltas cuando añades %w en capas superioresError() string satisface error.errors.As(err, &target) para enlazar el primer tipo coincidente.%w mantiene los tipos descubribles a través de las capas.| Elección | Beneficio | Riesgo |
|---|---|---|
| Struct de valor | Sin error de interfaz nil tipado | Copias al retornar (generalmente bien) |
| Struct de puntero | Mutación compartida (raro para errores) | Fácil de retornar nil tipado |
| Campo de cadena de código de error | Etiquetas de métricas estables | Necesita un enum documentado |
| Tipo de envoltura opaco | Encapsulación | Los llamadores dependen de As hacia el tipo exportado |
Las bibliotecas y la biblioteca estándar usan interfaces opcionales pequeñas comprobadas con aserciones de tipo o errors.As:
interface{ Timeout() bool } - decisiones de reintentointerface{ Temporary() bool } - obsoleto pero todavía visto en código antiguointerface{ Unwrap() error } - envolviendo (también satisfecho por errores fmt con %w)Define las tuyas cuando múltiples tipos de error compartan comportamiento sin una struct común.
// El constructor evita filtrar la disposición de la struct
func NewRateLimitError(retryAfter int) error {
return rateLimitError{retryAfter: retryAfter}
}
// Mal: puntero nil tipado como interfaz de error
func bad() error {
var p *APIError
return p // ¡interfaz no nil!
}var e *MyErr; return e produce un error no nil. Solución: Retorna nil o usa tipos de valor.Code para la lógica.Is/As a través de tu tipo. Solución: Implementa Unwrap() error o incrusta el error envuelto.As hacia MyErr vs *MyErr deben coincidir con los retornos. Solución: Elige un estilo por familia de tipos.| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| Variables centinela | Un único resultado fijo | Necesitas campos o metadatos |
Solo fmt.Errorf | Herramientas internas rápidas | Se requiere manejo programático estable |
status.Status de gRPC | Solo transporte gRPC | La capa de dominio debe permanecer agnóstica al transporte |
errors.Join | Múltiples fallos a nivel de campo | Un único fallo con una causa raíz |
Cuando los llamadores necesitan campos (estado, nombre de campo, reintento) o interfaces de comportamiento más allá de la identidad sí/no.
Exporta los tipos que forman parte de tu contrato. Mantén los errores de implementación internos no exportados con constructores.
Un mensaje conciso para humanos, a menudo incluyendo un código estable. La lógica del programa debe usar campos a través de errors.As, no análisis de cadenas.
Define métodos pequeños (HTTPStatus() int). Los manejadores usan errors.As hacia el tipo de interfaz para llamarlos sin listar cada struct.
Prefiere receptores de valor para structs de error a menos que tengas una razón específica para punteros. Los valores evitan los peligros del nil tipado.
Sí. Retorna el error envuelto para que Is/As atraviesen tu tipo.
Añade campos sin eliminar los exportados. Prefiere códigos nuevos en un campo de cadena en lugar de renombrar tipos.
Debatible. Un enfoque pragmático: HTTPStatus() opcional en tipos de capa de aplicación; mantén los paquetes de dominio agnósticos al transporte cuando se reutilicen entre CLI y HTTP.
Retorna errors.Join de múltiples valores ValidationError cuando varios campos fallan a la vez.
Raramente necesario. Las interfaces y As cubren la mayoría de los casos; los tipos de resultado genéricos (T, error) son más comunes.
Avanzado: implementa Format para salida detallada %+v (usada por algunas bibliotecas de registro). Opcional para diagnósticos de aplicaciones.
Usa errors.As en las pruebas para afirmar campos. Evita comparar cadenas Error() completas cuando los mensajes evolucionan.
Versiones de Stack: Esta página fue escrita para Go 1.26.x (predeterminado Green Tea GC, 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: 19 jul 2026