Skip to content
Advertisement

Traceback handling with on_application_command_error() vs. @command.error

I want to write an error handler for my commands that would send the traceback in my dms using Pycord version 2.0.0. I first tried setting up per-command handlers using @command.error:

# Libraries
import discord
from traceback import format_exc

# Project files
import logic

bot = discord.Bot()


@bot.slash_command(guild_ids=logic.dev_ids, name="exception-test",
                   description="Raises an exception to test error-handling.")
async def exception_test(ctx):
    raise ZeroDivisionError


@exception_test.error
async def exception_test_error(ctx, error):
    await ctx.respond("An exception has occurred! Sending traceback...")

    me = await bot.fetch_user(logic.MY_ID) or bot.get_user(logic.MY_ID)
    full_error = format_exc()

    await me.send(f"**An exception has occurred!** (User {ctx.user} used /"
                  f"{ctx.command.qualified_name} with args {ctx.selected_options})n```pyn{full_error}```")


bot.run(logic.ds_token)

This works perfectly fine (Message sent to DMs goes as follows):

An exception has occurred! (User MrQez#0333 used /exception-test with args None)

Traceback (most recent call last):
  File "C:UsersUserAppDataLocalProgramsPythonPython38libsite-packagesdiscordcommandscore.py", line 127, in wrapped
    ret = await coro(arg)
  File "C:UsersUserAppDataLocalProgramsPythonPython38libsite-packagesdiscordcommandscore.py", line 881, in _invoke
    await self.callback(ctx, **kwargs)
  File "E:/Data/exception-test.py", line 14, in exception_test
    raise ZeroDivisionError
ZeroDivisionError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:UsersUserAppDataLocalProgramsPythonPython38libsite-packagesdiscordbot.py", line 992, in invoke_application_command
    await ctx.command.invoke(ctx)
  File "C:UsersUserAppDataLocalProgramsPythonPython38libsite-packagesdiscordcommandscore.py", line 358, in invoke
    await injected(ctx)
  File "C:UsersUserAppDataLocalProgramsPythonPython38libsite-packagesdiscordcommandscore.py", line 135, in wrapped
    raise ApplicationCommandInvokeError(exc) from exc
discord.errors.ApplicationCommandInvokeError: Application Command raised an exception: ZeroDivisionError: 

However, as the amount of commands in my bot grew, the per-command approach was starting to become inefficient. So, I tried a more global approach:

@bot.event
async def on_application_command_error(ctx: discord.ApplicationContext, error: discord.DiscordException):
    await ctx.respond("An exception has occurred! Sending traceback...")

    me = await bot.fetch_user(logic.MY_ID) or bot.get_user(logic.MY_ID)
    full_error = format_exc()

    await me.send(f"**An exception has occurred!** (User {ctx.user} used /"
                  f"{ctx.command.qualified_name} with args {ctx.selected_options})n```pyn{full_error}```")

This returns a disappointing result:

An exception has occurred! (User MrQez#0333 used /exception-test with args None)

NoneType: None

Why do these error-handlers return a different result? And how can I fix my on_application_command_error solution so that it returns the traceback?

Advertisement

Answer

Explanation

A simple solution would be to, instead of getting the error from sys.exc_info (format_exc does this for you), just use the DiscordException provided to you.

Code

@bot.event
async def on_application_command_error(ctx: discord.ApplicationContext, error: discord.DiscordException):
    await ctx.respond("An exception has occurred! Sending traceback...")

    full_error = traceback.format_exception(error)

    await ctx.channel.send(f"**An exception has occurred!** (User {ctx.user} used /"
                  f"{ctx.command.qualified_name} with args {ctx.selected_options})n```pyn{''.join(full_error)}```")

In Python <3.10, replace

full_error = traceback.format_exception(error)

with

full_error = traceback.format_exception(type(error), error, error.__traceback__)

Reference

traceback.format_exception

Advertisement