Manejo de Errores en Go: Patrones, Wrapping y Mejores Practicas para 2026
Guia completa sobre el manejo de errores en Go: errores centinela, tipos personalizados, wrapping con fmt.Errorf, errors.Is y errors.As. Patrones profesionales para entrevistas tecnicas y desarrollo en produccion.

El manejo de errores en Go representa una de las caracteristicas mas distintivas del lenguaje. A diferencia de otros lenguajes que utilizan excepciones, Go adopta un enfoque explicito donde los errores son valores que deben manejarse de forma deliberada. Esta filosofia, aunque puede parecer verbosa al principio, resulta en codigo mas predecible y facil de depurar en sistemas de produccion.
En el ecosistema Go de 2026, dominar el manejo de errores no es opcional: es un requisito fundamental para cualquier desarrollador que aspire a construir aplicaciones robustas. Las entrevistas tecnicas para posiciones de backend con Go invariablemente incluyen preguntas sobre patrones de error, wrapping y las funciones del paquete errors.
Los entrevistadores evaluan tres aspectos del manejo de errores: comprension de la interfaz error, uso correcto de errors.Is vs errors.As, y capacidad para disenar jerarquias de errores en arquitecturas por capas. Este articulo cubre los tres en profundidad.
La Interfaz Error: Fundamento del Sistema
Todo el sistema de errores en Go se construye sobre una interfaz extraordinariamente simple definida en el paquete builtin:
type error interface {
Error() string
}Cualquier tipo que implemente el metodo Error() string satisface esta interfaz. Esta simplicidad permite una flexibilidad enorme: desde errores basicos creados con errors.New() hasta estructuras complejas que contienen metadata, stacktraces y errores anidados.
La convencion en Go establece que las funciones que pueden fallar retornan el error como ultimo valor de retorno. El codigo que llama a estas funciones debe verificar explicitamente si el error es nil antes de continuar. Este patron, aunque repetitivo, elimina la ambiguedad sobre que operaciones pueden fallar y obliga a los desarrolladores a considerar los casos de error desde el inicio.
Tipos de Error Personalizados
Cuando los mensajes de error simples resultan insuficientes, Go permite crear tipos de error personalizados que encapsulan informacion adicional:
type NotFoundError struct {
Resource string
ID string
}
func (e *NotFoundError) Error() string {
return fmt.Sprintf("%s with ID %s not found", e.Resource, e.ID)
}Los tipos personalizados ofrecen varias ventajas significativas. Primero, permiten adjuntar campos estructurados que facilitan el logging y la observabilidad. Segundo, habilitan la inspeccion programatica mediante errors.As. Tercero, proporcionan una forma type-safe de categorizar errores sin depender de comparaciones de strings.
En aplicaciones empresariales, es comun definir una jerarquia de errores de dominio que refleje las reglas de negocio. Un ValidationError puede contener una lista de campos invalidos, mientras que un AuthorizationError puede incluir el permiso requerido y el rol del usuario.
Errores Centinela: Valores Predefinidos
Los errores centinela son valores de error predefinidos a nivel de paquete que representan condiciones especificas:
var (
ErrNotFound = errors.New("record not found")
ErrUnauthorized = errors.New("unauthorized access")
ErrConflict = errors.New("resource conflict")
)Estos errores siguen la convencion de nomenclatura Err seguido de la condicion. La ventaja principal es que permiten comparaciones directas usando errors.Is, lo que resulta mas eficiente y legible que comparar mensajes de texto.
Sin embargo, los errores centinela tienen limitaciones. Al ser valores globales inmutables, no pueden contener informacion contextual dinamica. Por esta razon, muchos equipos combinan errores centinela con wrapping para agregar contexto mientras mantienen la capacidad de identificar el error original.
Error Wrapping: Agregando Contexto
El wrapping de errores, introducido en Go 1.13, permite envolver un error existente con informacion adicional mientras se preserva el error original:
func (r *UserRepo) FindByID(ctx context.Context, id string) (*User, error) {
user, err := r.db.QueryContext(ctx, "SELECT * FROM users WHERE id = $1", id)
if err != nil {
return nil, fmt.Errorf("UserRepo.FindByID(%s): %w", id, err)
}
return user, nil
}El verbo %w en fmt.Errorf es la clave del wrapping. A diferencia de %v que simplemente formatea el error como string, %w crea una cadena de errores que puede recorrerse posteriormente. Cada capa de la aplicacion puede agregar su propio contexto sin perder la informacion del error subyacente.
¿Listo para aprobar tus entrevistas de Go?
Practica con nuestros simuladores interactivos, flashcards y tests técnicos.
Un patron efectivo consiste en incluir el nombre de la funcion y los parametros relevantes en el mensaje de wrapping. Esto crea un pseudo-stacktrace que facilita enormemente la depuracion en logs de produccion, especialmente cuando el error atraviesa multiples capas de la aplicacion.
errors.Is: Comparacion de Errores
La funcion errors.Is verifica si un error especifico existe en alguna parte de la cadena de errores wrapped:
func handleGetUser(w http.ResponseWriter, r *http.Request) {
user, err := userService.GetByID(r.Context(), chi.URLParam(r, "id"))
if errors.Is(err, ErrNotFound) {
http.Error(w, "User not found", http.StatusNotFound)
return
}
if err != nil {
http.Error(w, "Internal error", http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(user)
}La funcion errors.Is recorre recursivamente la cadena de errores llamando al metodo Unwrap() de cada error hasta encontrar una coincidencia o llegar al final. Esto significa que incluso si el error original fue wrapped multiples veces, errors.Is puede identificarlo correctamente.
Para errores personalizados, es posible implementar el metodo Is(target error) bool para definir logica de comparacion personalizada. Esto resulta util cuando dos errores diferentes deben considerarse equivalentes bajo ciertas condiciones.
errors.As: Extraccion de Tipos
Mientras errors.Is compara valores, errors.As extrae tipos especificos de la cadena de errores:
func errorMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if r := recover(); r != nil {
http.Error(w, "Internal error", http.StatusInternalServerError)
}
}()
next.ServeHTTP(w, r)
})
}
func mapErrorToHTTP(err error) int {
var notFound *NotFoundError
if errors.As(err, ¬Found) {
return http.StatusNotFound
}
var validationErr *ValidationError
if errors.As(err, &validationErr) {
return http.StatusBadRequest
}
return http.StatusInternalServerError
}El segundo argumento de errors.As debe ser un puntero a la variable donde se almacenara el error extraido. Si la funcion retorna true, la variable contendra el error del tipo solicitado y se puede acceder a sus campos estructurados.
Este patron es fundamental para mapear errores de dominio a respuestas HTTP apropiadas. En lugar de depender de strings o codigos numericos, el codigo puede inspeccionar el tipo concreto del error y extraer informacion relevante para la respuesta.
Errores de Dominio en Arquitecturas por Capas
En aplicaciones con arquitectura limpia o hexagonal, es esencial definir errores de dominio que encapsulen tanto la categoria del error como informacion contextual:
type DomainError struct {
Code string
Message string
Err error
}
func (e *DomainError) Error() string {
if e.Err != nil {
return fmt.Sprintf("%s: %s: %v", e.Code, e.Message, e.Err)
}
return fmt.Sprintf("%s: %s", e.Code, e.Message)
}
func (e *DomainError) Unwrap() error {
return e.Err
}El metodo Unwrap() es crucial: permite que errors.Is y errors.As continuen su busqueda a traves del error envuelto. Sin este metodo, la cadena de errores se romperia en el DomainError.
En la capa de servicio, estos errores de dominio se utilizan para comunicar condiciones de negocio:
func (s *UserService) Deactivate(ctx context.Context, userID string) error {
user, err := s.repo.FindByID(ctx, userID)
if err != nil {
return fmt.Errorf("deactivating user %s: %w", userID, err)
}
if user.Status == StatusInactive {
return &DomainError{
Code: "ALREADY_INACTIVE",
Message: fmt.Sprintf("user %s is already inactive", userID),
}
}
return s.repo.UpdateStatus(ctx, userID, StatusInactive)
}Este enfoque separa claramente los errores tecnicos (problemas de base de datos, red, etc.) de los errores de dominio (reglas de negocio violadas), permitiendo que cada capa maneje los errores de forma apropiada.
Preguntas Frecuentes en Entrevistas Tecnicas
Las entrevistas para posiciones de Go frecuentemente incluyen preguntas sobre manejo de errores. Estas son algunas de las mas comunes:
Cual es la diferencia entre errors.Is y errors.As?
errors.Is compara un error con un valor especifico (tipicamente un error centinela), mientras que errors.As intenta extraer un error de un tipo especifico de la cadena. Se usa Is para valores conocidos y As para tipos que contienen informacion adicional.
Por que Go no usa excepciones? El equipo de Go considera que las excepciones obscurecen el flujo de control y hacen dificil razonar sobre que operaciones pueden fallar. Los errores como valores hacen explicito el manejo de errores y facilitan la revision de codigo.
Cuando usar panic vs error?
panic se reserva para situaciones verdaderamente excepcionales donde la aplicacion no puede continuar de forma segura, como errores de programacion o corrupcion de estado. Para condiciones de error esperadas, siempre se prefiere retornar un error.
Antipatrones que Deben Evitarse
Existen varios patrones problematicos que los desarrolladores deben reconocer y evitar:
// anti-pattern: silent error swallowing
result, _ := riskyOperation()Ignorar errores con el blank identifier es una de las practicas mas peligrosas. Si una operacion puede fallar, ese error debe manejarse o, como minimo, registrarse. El codigo que ignora errores sistematicamente genera bugs dificiles de diagnosticar.
// anti-pattern: double handling
if err != nil {
log.Printf("operation failed: %v", err)
return err
}El manejo doble ocurre cuando un error se registra y luego se retorna sin modificar. Esto resulta en logs duplicados cuando cada capa de la aplicacion repite el patron. La solucion es elegir: o se maneja el error (logging, metricas, etc.) o se propaga hacia arriba, pero no ambos.
Concurrencia y errgroup
Cuando se procesan operaciones concurrentes, el paquete errgroup proporciona una forma elegante de manejar errores:
func (s *OrderService) ProcessBatch(ctx context.Context, orderIDs []string) error {
g, ctx := errgroup.WithContext(ctx)
g.SetLimit(10)
for _, id := range orderIDs {
g.Go(func() error {
if err := s.processOrder(ctx, id); err != nil {
return fmt.Errorf("processing order %s: %w", id, err)
}
return nil
})
}
return g.Wait()
}El errgroup cancela automaticamente el contexto cuando cualquier goroutine retorna un error, deteniendo el trabajo restante de forma limpia. El metodo SetLimit controla la concurrencia maxima, previniendo la sobrecarga de recursos. Este patron es esencial para procesamiento batch en aplicaciones de produccion.
Go 1.24 introdujo mejoras en el paquete errors, incluyendo mejor soporte para multiples errores wrapped y optimizaciones de rendimiento en errors.Is y errors.As. Estas mejoras benefician especialmente a aplicaciones que manejan grandes volumenes de errores concurrentes.
Conclusion
El manejo de errores en Go, aunque inicialmente puede parecer verboso comparado con otros lenguajes, ofrece un modelo mental claro y predecible que escala bien en aplicaciones de produccion. Los puntos clave a recordar incluyen:
- La interfaz
errores simple por diseno: cualquier tipo con un metodoError() stringla satisface, permitiendo desde errores basicos hasta estructuras complejas con metadata - El wrapping con
%wpreserva la cadena de errores: cada capa puede agregar contexto sin perder informacion del error original errors.Ispara valores,errors.Aspara tipos: elegir la funcion correcta segun si se compara un valor centinela o se extrae un tipo especifico- Los errores de dominio separan logica de negocio de errores tecnicos: esto facilita el mapeo a respuestas HTTP y la toma de decisiones en cada capa
- Evitar antipatrones como el swallowing silencioso y el manejo doble: estos generan bugs sutiles y logs confusos
errgrouppara concurrencia segura: maneja errores en operaciones paralelas con cancelacion automatica
Dominar estos patrones no solo prepara a los desarrolladores para entrevistas tecnicas, sino que resulta en codigo Go mas robusto y mantenible en produccion.
¡Empieza a practicar!
Pon a prueba tu conocimiento con nuestros simuladores de entrevista y tests técnicos.
Etiquetas
Compartir
Artículos relacionados

Patrones de diseño en Go: patrones esenciales y preguntas de entrevista para desarrolladores Go
Domina los patrones de diseño de Go: Functional Options, Strategy, Factory y Observer. Ejemplos de código prácticos, buenas prácticas idiomáticas y preguntas de entrevista frecuentes para desarrolladores Go.

Top 25 preguntas de entrevista Go: guía completa para desarrolladores
Domina las entrevistas de Go con las 25 preguntas más frecuentes. Goroutines, channels, interfaces y patrones de concurrencia con ejemplos de código.

Preguntas de Entrevista sobre Go 1.26: Green Tea GC, go fix y Nuevas Optimizaciones
Guía completa sobre las características de Go 1.26 para entrevistas técnicas, incluyendo el recolector de basura Green Tea, optimizaciones de stack y nuevas herramientas del lenguaje.