aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Bots Discord

Gestion des erreurs de bot Discord : try/except et handlers

Une erreur de bot Discord surgit en général au pire moment : tard le soir, quand les utilisateurs sont les plus actifs, le bot cesse soudain de répondre aux commandes, un long traceback remplit le terminal et le processus s'arrête. La bonne nouvelle, c'est que la grande majorité de ces situations sont prévisibles et qu'avec une bonne gestion des erreurs vous pouvez garder votre bot en vie. Dans cet article, je vais aborder les types d'erreurs courants, l'usage de try/except au niveau d'une commande, la mise en place d'un handler global et un logging correct — côté discord.py comme côté discord.js.

Pourquoi la gestion des erreurs est essentielle

Un bot est un processus de longue durée. Une seule exception non capturée dans une commande va, selon le framework, soit faire échouer cette commande silencieusement, soit, dans le pire des cas, faire tomber tout le bot. Pour l'utilisateur, cela se traduit simplement par « le bot est cassé ». Une bonne stratégie de gestion des erreurs fait trois choses :

  • Isoler : une erreur dans une commande n'affecte pas les autres.
  • Informer : l'utilisateur reçoit un message clair, pas un traceback.
  • Enregistrer : en tant que développeur, vous voyez dans les logs ce qui a cassé et pourquoi.

Les erreurs les plus fréquentes

Avant de pouvoir gérer les erreurs, il faut les reconnaître. En pratique, voici celles que vous rencontrerez le plus :

  • Erreurs de permission : le bot tente de poster dans un salon ou d'attribuer un rôle sans en avoir le droit. En discord.py c'est discord.Forbidden ; en discord.js c'est 50013 Missing Permissions.
  • Rate limit (429) : trop de requêtes en trop peu de temps. Les bibliothèques les mettent généralement en file d'attente automatiquement, mais attention aux envois de messages dans une boucle.
  • Ressource introuvable (404) : réagir à un message supprimé, récupérer un membre qui n'existe plus.
  • Entrée utilisateur invalide : du texte là où un nombre était attendu, un argument manquant. En discord.py : BadArgument / MissingRequiredArgument.
  • Problèmes de token / gateway : un mauvais token, ou des intents privilégiés jamais activés.

try/except au niveau de la commande

La première ligne de défense est un bloc try/except qui enveloppe directement l'opération risquée. La règle clé : n'utilisez jamais un except: nu — capturez toujours l'exception précise que vous attendez et laissez remonter les autres pour que le handler global et les logs les voient.

import discord
from discord.ext import commands

bot = commands.Bot(command_prefix="!", intents=discord.Intents.default())

@bot.command()
async def kick(ctx, member: discord.Member):
    try:
        await member.kick(reason="Décision d'un modérateur")
        await ctx.send(f"{member} a été expulsé du serveur.")
    except discord.Forbidden:
        await ctx.send("Je n'ai pas la permission d'expulser ce membre.")
    except discord.HTTPException as e:
        await ctx.send("Une erreur réseau s'est produite pendant l'opération.")
        raise  # relancer pour que le logger et le handler global la voient

Ici nous expliquons à l'utilisateur les deux cas connus (pas de permission, erreur réseau) ; dans la branche discord.HTTPException, nous relançons l'erreur avec raise après avoir répondu, afin qu'elle parvienne aux logs.

Mettre en place un handler global

Écrire un try/except dans chaque commande n'est pas tenable. Définissez plutôt un capteur d'erreurs central. En discord.py, c'est l'événement on_command_error :

import logging
logger = logging.getLogger("bot")

@bot.event
async def on_command_error(ctx, error):
    if isinstance(error, commands.MissingRequiredArgument):
        await ctx.send(f"Argument manquant : {error.param.name}")
    elif isinstance(error, commands.BadArgument):
        await ctx.send("Vous avez saisi une valeur invalide.")
    elif isinstance(error, commands.CommandOnCooldown):
        await ctx.send(f"Veuillez patienter {error.retry_after:.1f}s.")
    elif isinstance(error, commands.MissingPermissions):
        await ctx.send("Vous n'avez pas la permission pour cette commande.")
    else:
        logger.exception("Erreur inattendue", exc_info=error)
        await ctx.send("Une erreur inattendue s'est produite et a été enregistrée.")

Si vous utilisez les slash commands (app commands), vous devrez assigner un handler distinct via tree.on_error ; le classique on_command_error des commandes à préfixe ne couvre pas les slash commands.

Côté discord.js, la logique est similaire : vous écoutez l'événement du client et gérez aussi les promesses rejetées non capturées :

client.on('interactionCreate', async interaction => {
  if (!interaction.isChatInputCommand()) return;
  try {
    await handleCommand(interaction);
  } catch (err) {
    console.error(err);
    const reply = { content: 'Une erreur est survenue.', ephemeral: true };
    if (interaction.replied || interaction.deferred) {
      await interaction.followUp(reply);
    } else {
      await interaction.reply(reply);
    }
  }
});

process.on('unhandledRejection', err => console.error('Unhandled:', err));

Un logging correct

Travailler avec print() paraît tentant au début, mais c'est inutilisable en production. Utilisez le module logging intégré de Python : il offre des niveaux (INFO, WARNING, ERROR), des horodatages et l'écriture dans un fichier.

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
    handlers=[
        logging.FileHandler("bot.log", encoding="utf-8"),
        logging.StreamHandler(),
    ],
)

Quelques conseils pratiques :

  • Utilisez logger.exception(...) là où vous capturez l'erreur ; cela ajoute le traceback automatiquement.
  • Ne loguez jamais de secrets comme les tokens ou mots de passe.
  • Faites tourner vos fichiers de logs (RotatingFileHandler) pour ne pas saturer le disque.
  • Envoyer les erreurs critiques vers un salon Discord dédié via webhook est un moyen pratique de détecter les problèmes tôt.

Rester en ligne sans planter

Même avec de bons handlers, votre bot peut tomber un jour. Faites donc tourner le processus sous un superviseur : un service systemd sous Linux, ou un gestionnaire de processus comme pm2, redémarrera le bot automatiquement à l'arrêt. Le but n'est pas de masquer le crash mais de lire les logs, corriger la cause racine et ne voir le superviseur que comme un filet de sécurité.

Questions fréquentes

Dois-je envelopper chaque commande dans un try/except ?

Non. Utilisez un try/except local uniquement là où vous voulez un message personnalisé, ou autour d'une opération risquée connue (suppression de messages, attribution de rôles). Pour tout le reste, le on_command_error central suffit.

Pourquoi les erreurs des slash commands ne sont-elles pas capturées ?

Parce que on_command_error ne couvre que les commandes à préfixe. Pour les slash (app) commands, vous devez définir séparément le handler d'erreur de l'arbre de commandes (tree.on_error).

Je reçois sans cesse des erreurs de rate limit — que faire ?

Évitez d'envoyer des messages à la suite dans une boucle, découpez les opérations en lots et faites confiance à la file d'attente intégrée de la bibliothèque. Plutôt que de ralentir chaque requête avec un sleep manuel, regroupez vos opérations.

Votre bot plante constamment ou vous n'arrivez pas à suivre ses erreurs ? Je peux vous aider à mettre en place une gestion des erreurs et un logging solides et à rendre votre bot prêt pour la production. Contactez-moi.

Bu kategorideki tüm yazılar →

Devamı için