Search code examples
inheritanceerror-handlingdiscord.pycustom-error-handlingpycord

Call global error handler only if the local command error handler didn't catch any error discord.py


Let's say I have a basic on_command_error event that acts as a global error handler:

@client.event
async def on_command_error(ctx, error):
    if isinstance(error, BadArgument):
        await ctx.reply("That input was wrong!")
        return
    elif isinstance(error, NotFound):
        await ctx.reply("I couldn't find that member!")
        return
    raise error

And I also have a command with a local error handler, like this:

@client.command()
async def number(ctx, amount):
    if not amount.isnumeric():
        raise BadArgument
    ctx.guild.get_member(1) #creating a NotFound error

@number.error
async def number_error(ctx, error):
    if isinstance(error, BadArgument):
        await ctx.reply("This is a special answer!")

Is it possible to make it so that, if the local error handler doesn't catch any error, then it calls the global error handler?

In this case, the ideal scenario would be: if the command raises BadArgument, then it should reply with "This is a special answer!" from the local error handler, and that's it.

But if it raises NotFound, then it should reply "I couldn't find that member!", from the global error handler.


Solution

  • Generic handlers are always called by discord.py after local handlers.

    In your case, it works as intended. number_error is called first, and then your global on_command_error is called.

    You can set a flag to ignore it, like this:

    @raise_exception.error
    async def raise_exception_error(ctx, err):
        if isinstance(err, commands.CommandInvokeError) and isinstance(err.__cause__, RuntimeError):
            await ctx.send('ignoring!')
            ctx._ignore_me_ = True
            return
        await ctx.send(f'propagating this error to `on_command_error`: `{err.__class__.__name__}: {err}`')
    
    @client.event
    async def on_command_error(ctx, err):
        if hasattr(ctx, '_ignore_me_'):
            await ctx.send('skipped!')
            return
        await ctx.send(f'caught in `on_command_error`: `{err.__class__.__name__}` -> `{err}`')
    

    That outputs:

    enter image description here

    In your case, you can use it like this:

    @number.error
    async def number_error(ctx, error):
        if isinstance(error, BadArgument):
            await ctx.reply("This is a special answer!")
            ctx._ignore_me_ = True
    
    @client.event
    async def on_command_error(ctx, error):
        if hasattr(ctx, '_ignore_me_'):
            return