# 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. - Published: 2026-06-15 - Updated: 2026-06-15 - Author: SharpSkill - Tags: go, error-handling, best-practices, interview, golang - Reading time: 9 min --- 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`. > **Clave para entrevistas tecnicas** > > 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`: ```go // builtin.go 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: ```go // apperror.go 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: ```go // errors.go 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: ```go // repository.go 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. 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: ```go // handler.go 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: ```go // middleware.go 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: ```go // domain/errors.go 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: ```go // service/user.go 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: ```go // 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. ```go // 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: ```go // batch.go 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. > **Novedades en Go 1.24** > > 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 `error` es simple por diseno**: cualquier tipo con un metodo `Error() string` la satisface, permitiendo desde errores basicos hasta estructuras complejas con metadata - **El wrapping con `%w` preserva la cadena de errores**: cada capa puede agregar contexto sin perder informacion del error original - **`errors.Is` para valores, `errors.As` para 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 - **`errgroup` para 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. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/es/blog/go/go-error-handling-patterns-wrapping-best-practices