Skip to content

Instantly share code, notes, and snippets.

@EvieePy
Last active April 3, 2025 19:21
Show Gist options
  • Save EvieePy/7822af90858ef65012ea500bcecf1612 to your computer and use it in GitHub Desktop.
Save EvieePy/7822af90858ef65012ea500bcecf1612 to your computer and use it in GitHub Desktop.
Simple Error Handling for Prefix and App commands - discord.py
import logging
import discord
from discord import app_commands
from discord.ext import commands
LOGGER: logging.Logger = logging.getLogger(__name__)
class CommandErrors(commands.Cog):
def __init__(self, bot: commands.Bot) -> None:
# Save the default handlers incase we have to unload this extension...
self.original_command_error = bot.on_command_error
self.original_app_error = bot.tree.on_error
# Assign the new handlers to the bot...
bot.on_command_error = self.on_command_error
bot.tree.on_error = self.on_app_command_error
self.bot = bot
async def cog_unload(self) -> None:
"""Load the original error handlers back into the bot when this Cog unloads."""
self.bot.on_command_error = self.original_command_error
self.bot.tree.on_error = self.original_app_error
async def send(
self,
content: str,
/,
context: commands.Context[commands.Bot] | discord.Interaction[commands.Bot],
ephemeral: bool = True
) -> None:
"""Simple generic method for sending a response.
This method catches and logs discord.HTTPExceptions.
Parameters
----------
content: str
The content to send in the response.
context: commands.Context | discord.Interaction
The context or interaction surrounding the command or app command.
ephemeral: bool
Optional bool indicating whether the repsonse should attempted to be sent ephemerally. Defaults to True.
"""
if isinstance(context, commands.Context):
send = context.send
else:
send = context.response.send_message if not context.response.is_done() else context.followup.send
try:
await send(content=content, ephemeral=ephemeral)
except discord.HTTPException as e:
msg = f'Ignoring HTTPException in %r for %r: %s\n\n'
LOGGER.error(msg, self.send.__name__, self.__class__.__name__, e, exc_info=e)
async def on_command_error(self, ctx: commands.Context[commands.Bot], error: commands.CommandError) -> None:
"""Prefix command error handler."""
# Don't continue if we have a command specific handler...
if ctx.command and ctx.command.has_error_handler():
return
# Don't continue if we have a Cog error handler...
elif ctx.cog and ctx.cog.has_error_handler():
return
error = getattr(error, 'original', error)
ignored = (commands.CommandNotFound, commands.NotOwner)
# Don't continue if the error is in ignored...
if isinstance(error, ignored):
return
# Example of handling some command errors...
# If we don't handle the Exception we log it to console...
elif isinstance(error, commands.NoPrivateMessage):
await self.send("This command can not be used in Private Messages.", context=ctx)
elif isinstance(error, commands.DisabledCommand):
await self.send("This command has been disabled by the owner.", context=ctx)
else:
LOGGER.error("Ignoring exception in command %s", ctx.command, exc_info=error)
async def on_app_command_error(
self,
interaction: discord.Interaction[commands.Bot],
error: app_commands.AppCommandError,
) -> None:
"""AppCommand error handler."""
command = interaction.command
# Default logging if the AppCommand is None...
if command is None:
LOGGER.error("Ignoring exception in command tree", exc_info=error)
return
# Don't continue if we have a command specific handler...
if command.on_error:
return
# Example of responding to an error...
if isinstance(error, app_commands.CommandOnCooldown):
retry_after = error.cooldown.get_retry_after()
await self.send(f"This command is on cooldown. Try again in {retry_after:.2f} seconds...", context=interaction)
else:
# Respond to all unhandled errors as an example... Could be removed to show the default response from Discord.
# All unhandled errors are also logged to console...
await self.send(f"**This application received an unhandled exception:**\n\n{error}", context=interaction)
LOGGER.error('Ignoring exception in command %r', command.name, exc_info=error)
async def setup(bot: commands.Bot) -> None:
await bot.add_cog(CommandErrors(bot))
import logging
import discord
from discord import app_commands
from discord.ext import commands
LOGGER: logging.Logger = logging.getLogger(__name__)
class CommandErrors(commands.Cog):
def __init__(self, bot: commands.Bot) -> None:
self.original_command_error = bot.on_command_error
self.original_app_error = bot.tree.on_error
bot.on_command_error = self.on_command_error
bot.tree.on_error = self.on_app_command_error
self.bot = bot
async def cog_unload(self) -> None:
"""Load the original error handlers back into the bot when this Cog unloads."""
self.bot.on_command_error = self.original_command_error
self.bot.tree.on_error = self.original_app_error
async def send(
self,
content: str,
/,
context: commands.Context[commands.Bot] | discord.Interaction[commands.Bot],
ephemeral: bool = True
) -> None:
"""Simple generic method for sending a response.
Parameters
----------
content: str
The content to send in the response.
context: commands.Context | discord.Interaction
The context or interaction surrounding the command or app command.
ephemeral: bool
Optional bool indicating whether the repsonse should attempted to be sent ephemerally. Defaults to True.
"""
if isinstance(context, commands.Context):
send = context.send
else:
send = context.response.send_message if not context.response.is_done() else context.followup.send
try:
await send(content=content, ephemeral=ephemeral)
except discord.HTTPException as e:
msg = f'Ignoring HTTPException in %r for %r: %s\n\n'
LOGGER.error(msg, self.send.__name__, self.__class__.__name__, e, exc_info=e)
async def on_command_error(self, ctx: commands.Context[commands.Bot], error: commands.CommandError) -> None:
"""Prefix command error handler."""
if ctx.command and ctx.command.has_error_handler():
return
elif ctx.cog and ctx.cog.has_error_handler():
return
error = getattr(error, 'original', error)
ignored = (commands.CommandNotFound, commands.NotOwner)
if isinstance(error, ignored):
return
elif isinstance(error, commands.NoPrivateMessage):
await self.send("This command can not be used in Private Messages.", context=ctx)
elif isinstance(error, commands.DisabledCommand):
await self.send("This command has been disabled by the owner.", context=ctx)
else:
LOGGER.error("Ignoring exception in command %s", ctx.command, exc_info=error)
async def on_app_command_error(
self,
interaction: discord.Interaction[commands.Bot],
error: app_commands.AppCommandError,
) -> None:
"""AppCommand error handler."""
command = interaction.command
if command is None:
LOGGER.error("Ignoring exception in command tree", exc_info=error)
return
if command.on_error:
return
if isinstance(error, app_commands.CommandOnCooldown):
retry_after = error.cooldown.get_retry_after()
await self.send(f"This command is on cooldown. Try again in {retry_after:.2f} seconds...", context=interaction)
else:
await self.send(f"**This application received an unhandled exception:**\n\n{error}", context=interaction)
LOGGER.error('Ignoring exception in command %r', command.name, exc_info=error)
async def setup(bot: commands.Bot) -> None:
await bot.add_cog(CommandErrors(bot))
@modelmat
Copy link

Hey, another example to prevent for the use of on_command_error occurring when __error defined in cogs, use

if hasattr(bot.get_cog(ctx.command.cog_name), '_' + ctx.command.cog_name + '__error'):
        return

Also maybe include __error for in cogs.

@EvieePy
Copy link
Author

EvieePy commented Mar 2, 2018

@modelmat

Hey, another example to prevent for the use of on_command_error occurring when __error defined in cogs, use

Since this is probably a niche thing, and slightly confusing to those who are not familiar with name mangling, I will keep it from the actual examples. Though should someone want to add that to the global handler...

if hasattr(ctx.cog, f'_{ctx.cog.__class__.__name__}__error'):
    ...  # Do whatever...

Would be a much simpler way of doing this.

@ClementJ18
Copy link

The MissingRequiredArgument if statements need to be updated, I'm not sure what changed, whether it was the shift to rewrite or some update of the inspect lib but error.param is a inspect.Parameter object and therefore will always return false in rewrite when compared to a string.

if error.param == 'inp':

has to be replaced by

if error.param.name == 'inp':

@laggycomputer
Copy link

Grammar error on L41.

@jkdz12
Copy link

jkdz12 commented Mar 3, 2019

Since 02/23/2019 this Error Handler doesn't work due the cog changes.
So I suggest you to updating:

    @commands.Cog.listener()
    async def on_command_error(self, ctx, error):
class CommandErrorHandler(commands.Cog):

@fleesu
Copy link

fleesu commented Mar 9, 2019

It's an easter egg!

@Moire9
Copy link

Moire9 commented Aug 10, 2020

What is this licensed under?

@TriHydera
Copy link

Grammar error on L41.

That's try:

@justanothernoob4648
Copy link

The printing default traceback thing doesn't work

Copy link

ghost commented Oct 10, 2020

The printing default traceback thing doesn't work

Yeah doesn't work me either

@tomlin7
Copy link

tomlin7 commented Oct 27, 2020

@hello.error
async def hello_error(self, ctx, error):

is it the right way of having arguments

@Shri2007
Copy link

mine not working

Copy link

ghost commented Mar 7, 2021

mine not working

are you gonna give an error? or say what it is doing? or show your source so people can see what you messed up?

@MrKomodoDragon
Copy link

could this possibly be updated because command.has_error_handler and cog.has_error_handler exists?

@frogmasta
Copy link

frogmasta commented Apr 27, 2021

Using these, we can just put this in the main on_command_error():

if ctx.command.has_error_handler() or ctx.cog.has_error_handler():
    return

@Daudd
Copy link

Daudd commented Apr 27, 2021

Using these, we can just put this in the main on_command_error():

if ctx.command.has_error_handler or ctx.cog.has_error_handler:
    return

Isn't it should be has_error_handler()?

@benjamonnguyen
Copy link

Does anyone know how to handle this error?
'Forbidden: 403 Forbidden (error code: 50001): Missing Access'

@frogmasta
Copy link

@Daudd yea, you're right. Fixed my code.

@krishna0223
Copy link

how to use this in my bot
i mean how to import this

@ImNimboss
Copy link

@benjamonnguyen You don't have the right permissions to do whatever you're trying to do

@bsod2528
Copy link

bsod2528 commented Oct 5, 2021

how to use this in my bot i mean how to import this

you dont have to import anything to make use of it. just make sure that its in your cogs folder

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment