# Gestion des Erreurs en Go : Patterns, Wrapping et Bonnes Pratiques en 2026 > Guide complet sur la gestion des erreurs en Go : types personnalisés, sentinel errors, error wrapping avec errors.Is et errors.As, anti-patterns à éviter et traitement concurrent avec errgroup. - Published: 2026-06-15 - Updated: 2026-06-15 - Author: SharpSkill - Tags: go, error-handling, best-practices, interview, golang - Reading time: 9 min --- La gestion des erreurs constitue l'un des aspects les plus distinctifs du langage Go. Contrairement aux exceptions utilisées dans de nombreux langages, Go adopte une approche explicite où chaque erreur potentielle doit être traitée au moment où elle survient. Cette philosophie, parfois critiquée pour sa verbosité, garantit un code robuste et prévisible lorsqu'elle est correctement appliquée. En 2026, les patterns de gestion d'erreurs en Go ont considérablement mûri. L'introduction de `errors.Is` et `errors.As` dans Go 1.13, combinée aux améliorations successives du runtime, offre désormais un arsenal complet pour gérer les erreurs de manière élégante et maintenable. Cet article explore les patterns essentiels que tout développeur Go doit maîtriser. > **Préparation aux entretiens techniques** > > La gestion des erreurs représente un sujet fréquemment abordé lors des entretiens Go. Les recruteurs évaluent non seulement la connaissance des APIs standards, mais aussi la capacité à concevoir des stratégies d'erreurs cohérentes à l'échelle d'une application. ## L'interface error : fondement du système L'interface `error` en Go se distingue par sa simplicité remarquable. Elle ne définit qu'une seule méthode : ```go // builtin.go type error interface { Error() string } ``` Cette simplicité constitue à la fois une force et une responsabilité. N'importe quel type implémentant la méthode `Error() string` satisfait l'interface. Cette flexibilité permet de créer des types d'erreurs riches en informations contextuelles, tout en maintenant une compatibilité universelle avec l'écosystème Go. Le pattern idiomatique de retour d'erreur place systématiquement l'erreur en dernière position des valeurs de retour. Cette convention, profondément ancrée dans la culture Go, facilite la lecture du code et permet d'identifier immédiatement les fonctions susceptibles d'échouer. ## Types d'erreurs personnalisés Les types d'erreurs personnalisés permettent d'encapsuler des informations structurées sur la nature de l'erreur. Ils s'avèrent particulièrement utiles lorsque le contexte de l'erreur influence la logique de traitement : ```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) } ``` Ce pattern offre plusieurs avantages significatifs. Le code appelant peut extraire les informations structurées (Resource, ID) pour personnaliser sa réponse. Les tests unitaires peuvent vérifier précisément le type et le contenu des erreurs. La documentation du code s'enrichit naturellement des champs exposés par le type. La convention recommande d'utiliser des pointeurs de structure comme receveurs pour les méthodes `Error()`. Cette approche évite les allocations inutiles et permet la comparaison par identité lorsque nécessaire. ## Sentinel errors : constantes d'erreur Les sentinel errors représentent des valeurs d'erreur prédéfinies au niveau du package. Elles servent de points de référence pour identifier des conditions d'erreur spécifiques : ```go // errors.go var ( ErrNotFound = errors.New("record not found") ErrUnauthorized = errors.New("unauthorized access") ErrConflict = errors.New("resource conflict") ) ``` La convention de nommage `Err` suivie d'un nom descriptif facilite l'identification de ces valeurs dans le code. Les sentinel errors conviennent particulièrement aux cas où l'existence même de l'erreur suffit, sans nécessiter d'informations contextuelles supplémentaires. L'utilisation judicieuse des sentinel errors améliore la lisibilité du code en établissant un vocabulaire commun au sein d'un package ou d'une application. Les consommateurs du package peuvent ainsi s'appuyer sur ces valeurs exportées pour structurer leur logique de gestion d'erreurs. ## Error wrapping : préserver le contexte L'error wrapping permet d'enrichir une erreur existante avec du contexte additionnel tout en préservant l'erreur originale. Le verbe `%w` dans `fmt.Errorf` crée cette relation de wrapping : ```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 } ``` Ce pattern construit une chaîne d'erreurs qui documente le chemin d'exécution ayant mené à l'échec. Chaque couche applicative ajoute son contexte spécifique, créant ainsi une trace exploitable pour le débogage. La clé réside dans l'équilibre entre information et bruit. Le contexte ajouté doit apporter une valeur diagnostique sans dupliquer les informations déjà présentes dans l'erreur sous-jacente. L'inclusion du nom de la fonction et des paramètres clés constitue généralement une bonne pratique. ## errors.Is : vérification d'identité d'erreur La fonction `errors.Is` parcourt la chaîne d'erreurs wrappées pour déterminer si une erreur spécifique s'y trouve. Elle remplace avantageusement les comparaisons directes qui échouaient avec les erreurs wrappées : ```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) } ``` L'ordre des vérifications importe. Les erreurs les plus spécifiques doivent être testées en premier, suivies des cas généraux. La vérification `err != nil` en dernier attrape toutes les erreurs non identifiées. La fonction `errors.Is` fonctionne correctement avec les sentinel errors et tout type implémentant la méthode `Is(error) bool` pour une logique de comparaison personnalisée. ## errors.As : extraction de type d'erreur Lorsque le traitement nécessite d'accéder aux champs d'un type d'erreur spécifique, `errors.As` permet d'extraire et de convertir l'erreur vers ce type : ```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 } ``` La variable cible doit être un pointeur vers le type recherché. En cas de succès, `errors.As` assigne l'erreur trouvée à cette variable, permettant ensuite d'accéder à ses champs et méthodes spécifiques. Ce mécanisme s'avère particulièrement puissant pour centraliser la logique de mapping entre erreurs domaine et réponses HTTP dans un middleware ou une fonction utilitaire. ## Erreurs domaine structurées Les applications d'envergure bénéficient d'un type d'erreur domaine standardisé. Ce type encapsule un code d'erreur, un message lisible et optionnellement l'erreur sous-jacente : ```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 } ``` La méthode `Unwrap` permet à `errors.Is` et `errors.As` de traverser la chaîne d'erreurs correctement. Sans cette méthode, l'erreur encapsulée dans le champ `Err` resterait inaccessible aux fonctions d'inspection. Les codes d'erreur (ALREADY_INACTIVE, VALIDATION_FAILED, etc.) facilitent l'internationalisation et le logging structuré. Ils permettent également aux clients API de réagir programmatiquement aux différentes conditions d'erreur. ## Couche service et propagation d'erreurs La couche service orchestre la logique métier et constitue un point stratégique pour la création et la propagation d'erreurs : ```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) } ``` Cette approche distingue clairement les erreurs techniques (provenant du repository) des erreurs métier (violation de règles domaine). Les premières sont wrappées pour préserver le contexte, tandis que les secondes sont créées explicitement avec les informations pertinentes. ## Questions fréquentes en entretien technique Les entretiens techniques Go abordent régulièrement ces aspects de la gestion d'erreurs : **Quelle différence entre errors.Is et errors.As ?** `errors.Is` vérifie si une erreur spécifique existe dans la chaîne (comparaison d'identité), tandis que `errors.As` tente d'extraire une erreur d'un type donné (conversion de type). Le premier répond à "cette erreur est-elle présente ?", le second à "puis-je accéder aux détails de ce type d'erreur ?". **Quand utiliser un type personnalisé versus un sentinel error ?** Les types personnalisés conviennent lorsque l'erreur transporte des données structurées nécessaires au traitement. Les sentinel errors suffisent quand seule l'existence de l'erreur importe, sans contexte additionnel. **Comment gérer les erreurs dans les goroutines ?** Les channels ou le package `errgroup` permettent de collecter et propager les erreurs des goroutines vers la goroutine principale. L'errgroup offre une API plus ergonomique avec annulation automatique du contexte. ## Anti-patterns à éviter Certaines pratiques nuisent à la maintenabilité et à la fiabilité du code. L'ignoration silencieuse des erreurs constitue le piège le plus courant : ```go // anti-pattern: silent error swallowing result, _ := riskyOperation() ``` Cette pratique masque des échecs potentiellement critiques. Si l'erreur ne peut être traitée localement, elle doit être propagée au niveau supérieur. Le double traitement représente un autre anti-pattern fréquent : ```go // anti-pattern: double handling if err != nil { log.Printf("operation failed: %v", err) return err } ``` Logger puis retourner l'erreur provoque des logs dupliqués lorsque chaque niveau de la pile d'appels adopte cette approche. La règle recommande de traiter l'erreur (logger, convertir, etc.) ou de la propager, mais pas les deux simultanément. ## Traitement concurrent avec errgroup Le package `golang.org/x/sync/errgroup` simplifie la gestion des erreurs dans les traitements concurrents. Il coordonne plusieurs goroutines et collecte leurs erreurs : ```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() } ``` L'errgroup annule automatiquement le contexte dès qu'une goroutine retourne une erreur, permettant aux autres goroutines de s'interrompre proprement. La méthode `SetLimit` contrôle le parallélisme pour éviter la saturation des ressources. La méthode `Wait` bloque jusqu'à la terminaison de toutes les goroutines et retourne la première erreur rencontrée. Pour collecter toutes les erreurs, des patterns alternatifs utilisant des channels ou le type `errors.Join` de Go 1.20+ s'avèrent nécessaires. > **Nouveautés Go 1.24** > > Go 1.24 introduit des améliorations au niveau du runtime pour le traitement des erreurs, notamment une meilleure intégration avec les outils de tracing distribué. Les stack traces attachées aux erreurs wrappées bénéficient également d'optimisations de performance significatives. ## Conclusion La gestion des erreurs en Go repose sur des principes simples mais exigeants. Une application robuste nécessite une stratégie cohérente appliquée à travers toutes les couches du code. Les points essentiels à retenir : - L'interface `error` offre une flexibilité maximale grâce à sa simplicité - Les types personnalisés encapsulent le contexte structuré nécessaire au traitement - Les sentinel errors établissent un vocabulaire commun pour les conditions d'erreur standard - L'error wrapping avec `%w` préserve la chaîne causale pour le débogage - `errors.Is` et `errors.As` permettent l'inspection robuste des erreurs wrappées - L'errgroup simplifie la collecte d'erreurs dans les traitements concurrents - Éviter l'ignoration silencieuse et le double traitement des erreurs La maîtrise de ces patterns distingue le code Go professionnel du code amateur. En entretien comme en production, une gestion d'erreurs rigoureuse témoigne d'une compréhension approfondie du langage et de ses idiomes. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/fr/blog/go/go-error-handling-patterns-wrapping-best-practices