Aikido

Pourquoi il faut gérer les erreurs dans les blocs catch au lieu de les laisser vides

Lisibilité

Règle
Gestion des erreurs dans catch . 
Les blocs catch vides silencieusement avaler erreurs, 
rendant débogage difficile. 
Langues prises en charge : Java, C, C++, PHP, JavaScript, 
TypeScript, Go, Python

Introduction

Les blocs `catch` vides sont l'un des anti-patterns les plus dangereux dans le code de production. Lorsque des exceptions sont capturées mais non gérées, l'erreur disparaît sans laisser de trace. L'application continue de fonctionner avec un état corrompu, des données invalides ou des opérations échouées qui auraient dû arrêter l'exécution. Les utilisateurs constatent des échecs silencieux où les fonctionnalités ne fonctionnent pas, mais ne reçoivent aucun message d'erreur. Les équipes d'exploitation n'ont pas de journaux pour le débogage. La seule indication qu'un problème existe n'apparaît que des heures ou des jours plus tard, lorsque des défaillances en cascade rendent le système inutilisable.

Pourquoi c'est important

Débogage et réponse aux incidents : Les blocs catch vides éliminent les journaux d'erreurs. Les ingénieurs n'ont aucune trace de pile, message d'erreur ou indication du moment ou de l'endroit où la défaillance s'est produite, ce qui rend les problèmes presque impossibles à reproduire.

Corruption silencieuse des données : Lorsque les opérations de base de données ou les appels d'API échouent à l'intérieur de blocs `catch` vides, l'application continue comme si elles avaient réussi. Les enregistrements sont partiellement mis à jour, les transactions sont incomplètes, et au moment où la corruption est découverte, la piste d'audit a disparu.

Vulnérabilités de sécurité : Les blocs catch vides masquent les échecs de sécurité tels que les erreurs d'authentification ou les vérifications d'autorisation. Un attaquant déclenchant une exception dans un chemin critique pour la sécurité pourrait contourner entièrement les protections si l'erreur est silencieusement ignorée.

Défaillances en cascade : Lorsque les erreurs sont ignorées, l'application continue dans un état invalide. Les opérations ultérieures dépendant du résultat de l'opération échouée échoueront également, créant une chaîne de défaillances qui éloigne les ingénieurs de la cause première réelle.

Exemples de code

❌ Non conforme :

async function updateUserProfile(userId, profileData) {
    try {
        await db.users.update(userId, profileData);
        await cache.invalidate(`user:${userId}`);
        await searchIndex.update(userId, profileData);
    } catch (error) {
        // TODO: handle error
    }

    return { success: true };
}

Pourquoi c'est incorrect : Si une opération échoue, l'erreur est silencieusement ignorée et la fonction renvoie un succès. La base de données pourrait être mise à jour mais l'invalidation du cache pourrait échouer, laissant des données obsolètes. Ou la mise à jour de l'index de recherche échoue, rendant l'utilisateur introuvable, sans aucun log ou alerte pour indiquer le problème.

✅ Conforme :

async function updateUserProfile(userId, profileData) {
    try {
        await db.users.update(userId, profileData);
        await cache.invalidate(`user:${userId}`);
        await searchIndex.update(userId, profileData);
        return { success: true };
    } catch (error) {
        logger.error('Failed to update user profile', {
            userId,
            error: error.message,
            stack: error.stack
        });
        throw new ProfileUpdateError(
            'Unable to update profile',
            { cause: error }
        );
    }
}

Pourquoi c'est important : Chaque erreur est journalisée avec son contexte, fournissant des informations de débogage. L'erreur se propage à l'appelant, permettant une gestion appropriée des erreurs au niveau adéquat. Les systèmes de surveillance peuvent alerter sur ces erreurs, et l'application échoue rapidement plutôt que de continuer avec un état invalide.

Conclusion

Les blocs `catch` vides ne sont jamais acceptables dans le code de production. Chaque exception capturée doit au minimum être journalisée, et la plupart doivent être propagées aux appelants ou déclencher des actions de récupération spécifiques. Si vous devez réellement ignorer une erreur, documentez la raison avec un commentaire expliquant la justification métier. Le comportement par défaut doit toujours être de gérer les erreurs explicitement, et non de les ignorer silencieusement.

FAQ

Des questions ?

Que se passe-t-il si je dois réellement ignorer certaines erreurs ?

Documentez-le explicitement avec un commentaire expliquant pourquoi l'erreur peut être ignorée en toute sécurité. Enregistrez l'erreur au niveau de débogage afin qu'elle apparaisse dans les journaux détaillés sans déclencher d'alertes. Demandez-vous si ignorer l'erreur pourrait entraîner un état invalide. Même pour les erreurs « attendues » comme les échecs de cache ou les délais d'attente réseau, la journalisation aide les équipes d'exploitation à comprendre les modèles de comportement du système.

Dois-je toujours journaliser les erreurs dans les blocs `catch` ?

Le logging est généralement une bonne idée, car vous ne pouvez pas déboguer les problèmes sans voir ce qui a échoué. Il existe des cas où vous pouvez tracer le problème sans logs, comme relancer immédiatement l'erreur pour qu'elle soit gérée ailleurs, ou si l'application doit planter et redémarrer en cas de défaillances critiques. Mais un logging approprié est toujours utile.

Quelle est la différence entre la journalisation et la relance d'erreurs ?

Le logging enregistre ce qui s'est passé pour le débogage et le monitoring. La relance propage l'erreur aux appelants afin qu'ils puissent décider comment y répondre. Faites les deux : loggez l'erreur avec son contexte au point de défaillance, puis relancez-la (éventuellement enveloppée dans un type d'erreur plus spécifique) pour permettre aux appelants de gérer la récupération. Ne loggez pas la même erreur à plusieurs niveaux, cela crée du bruit.

Comment gérer les erreurs qui se produisent dans les blocs finally ?

Les blocs `finally` devraient rarement lever des erreurs. S'ils doivent effectuer des opérations sujettes aux erreurs (comme la fermeture de ressources), enveloppez-les dans leur propre bloc `try-catch`. Enregistrez toutes les erreurs, mais ne les laissez pas masquer l'erreur originale. Certains langages fournissent une syntaxe pour gérer à la fois l'erreur principale et les erreurs des blocs `finally`, utilisez ces mécanismes pour préserver les deux contextes d'erreur.

Qu'en est-il de l'impact sur les performances de la journalisation de chaque erreur ?

Le logging est peu coûteux comparé au coût du débogage des problèmes de production sans logs. Les frameworks de logging modernes sont hautement optimisés. Si vous avez tant d'erreurs que le logging impacte les performances, corrigez les erreurs plutôt que de les masquer. Des taux d'erreur élevés indiquent des problèmes graves que les blocs catch vides ne feront qu'aggraver.

Les blocs `catch` doivent-ils toujours lever des erreurs, ou peuvent-ils retourner des valeurs d'erreur ?

Cela dépend du langage et de l'architecture. En JavaScript avec des promesses, lancer une exception depuis un bloc `catch` la propage au gestionnaire d'erreurs suivant. Retourner un objet d'erreur depuis un bloc `catch` résout la promesse avec cette erreur, ce qui est généralement incorrect. Familiarisez-vous avec la sémantique de gestion des erreurs de votre langage. Généralement, laissez les erreurs se propager à moins que vous ne puissiez les récupérer de manière significative.

Comment gérer les erreurs dans les opérations asynchrones qui n'ont pas de try-catch ?

Utilisez des gestionnaires .catch() sur les promesses, des écouteurs d'événements d'erreur sur les émetteurs d'événements, ou des callbacks d'erreur dans les API basées sur des callbacks. Ne jamais ignorer les gestionnaires de rejet ou les callbacks d'erreur. Les rejets de promesses non gérés doivent être surveillés au niveau du processus et traités comme des échecs critiques. Node.js moderne peut se terminer en cas de rejets non gérés, ce qui est préférable à une défaillance silencieuse.

Sécurisez-vous maintenant.

Sécuriser votre code, votre cloud et votre runtime dans un système centralisé unique.
Détectez et corrigez les vulnérabilités rapidement et automatiquement.

Pas de carte de crédit requise | Résultats du scan en 32 secondes.