CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-disnake

A modern, easy-to-use, feature-rich async-ready API wrapper for Discord written in Python

Pending
Overview
Eval results
Files

error-handling.mddocs/

Error Handling

Comprehensive error handling system covering all Discord API errors, command framework errors, interaction errors, and custom exception types with proper error recovery patterns and best practices for robust Discord bot development.

Capabilities

Base Discord Exceptions

Core exception hierarchy for all Discord-related errors and failures.

class DiscordException(Exception):
    """Base exception class for all Discord-related errors."""

class ClientException(DiscordException):
    """Exception for client operation failures."""

class InvalidData(ClientException):
    """Exception for malformed or invalid data from Discord API."""

class InvalidArgument(ClientException):
    """Exception for invalid arguments passed to functions."""

class LoginFailure(ClientException):
    """Exception for bot authentication failures."""

class NoMoreItems(ClientException):
    """Exception when async iterator has no more items."""

class GatewayNotFound(DiscordException):
    """Exception when gateway URL cannot be found."""

class ConnectionClosed(ClientException):
    """Exception when WebSocket connection is closed."""
    
    def __init__(self, socket: WebSocketClientProtocol, *, shard_id: Optional[int] = None):
        """
        Initialize connection closed exception.
        
        Parameters:
        - socket: WebSocket connection
        - shard_id: Shard ID if applicable
        """
    
    code: int
    reason: str
    shard_id: Optional[int]

class PrivilegedIntentsRequired(ClientException):
    """Exception for missing privileged intents."""
    
    def __init__(self, shard_id: Optional[int] = None):
        """
        Initialize privileged intents exception.
        
        Parameters:
        - shard_id: Shard ID if applicable
        """
    
    shard_id: Optional[int]

class SessionStartLimitReached(ClientException):
    """Exception when session start limit is reached."""

HTTP Exceptions

HTTP request and API error handling for Discord REST API interactions.

class HTTPException(DiscordException):
    """Exception for HTTP request failures."""
    
    def __init__(self, response: ClientResponse, message: Union[str, Dict[str, Any]]):
        """
        Initialize HTTP exception.
        
        Parameters:
        - response: HTTP response object
        - message: Error message or error data
        """
    
    response: ClientResponse
    status: int
    code: int
    text: str

class Forbidden(HTTPException):
    """Exception for 403 Forbidden HTTP errors."""

class NotFound(HTTPException):
    """Exception for 404 Not Found HTTP errors."""

class DiscordServerError(HTTPException):
    """Exception for 5xx server errors from Discord."""

class RateLimited(HTTPException):
    """Exception for rate limit errors (429)."""
    
    def __init__(self, response: ClientResponse, message: Dict[str, Any]):
        """
        Initialize rate limit exception.
        
        Parameters:
        - response: HTTP response
        - message: Rate limit data
        """
    
    retry_after: float
    is_global: bool

Interaction Exceptions

Errors specific to Discord interactions and UI components.

class InteractionException(DiscordException):
    """Base exception for interaction-related errors."""

class InteractionResponded(InteractionException):
    """Exception when interaction has already been responded to."""
    
    def __init__(self, interaction: Interaction):
        """
        Initialize interaction responded exception.
        
        Parameters:
        - interaction: The interaction that was already responded to
        """
    
    interaction: Interaction

class InteractionNotResponded(InteractionException):
    """Exception when interaction has not been responded to."""
    
    def __init__(self, interaction: Interaction):
        """
        Initialize interaction not responded exception.
        
        Parameters:
        - interaction: The interaction that needs a response
        """
    
    interaction: Interaction

class InteractionTimedOut(InteractionException):
    """Exception when interaction times out."""
    
    def __init__(self, interaction: Interaction):
        """
        Initialize interaction timeout exception.
        
        Parameters:
        - interaction: The timed out interaction
        """
    
    interaction: Interaction

class InteractionNotEditable(InteractionException):
    """Exception when interaction response cannot be edited."""
    
    def __init__(self, interaction: Interaction):
        """
        Initialize interaction not editable exception.
        
        Parameters:
        - interaction: The non-editable interaction
        """
    
    interaction: Interaction

class ModalChainNotSupported(InteractionException):
    """Exception when modal chaining is attempted but not supported."""

class WebhookTokenMissing(DiscordException):
    """Exception when webhook token is required but missing."""

class LocalizationKeyError(DiscordException):
    """Exception for localization key errors."""
    
    def __init__(self, key: str):
        """
        Initialize localization key error.
        
        Parameters:
        - key: The missing localization key
        """
    
    key: str

Command Framework Exceptions

Comprehensive error handling for the message-based command framework.

class CommandError(DiscordException):
    """Base exception for command-related errors."""
    
    def __init__(self, message: str = None, *args):
        """
        Initialize command error.
        
        Parameters:
        - message: Error message
        - args: Additional arguments
        """

class CommandNotFound(CommandError):
    """Exception when command is not found."""

class MissingRequiredArgument(UserInputError):
    """Exception for missing required command arguments."""
    
    def __init__(self, param: Parameter):
        """
        Initialize missing argument exception.
        
        Parameters:
        - param: Missing parameter information
        """
    
    param: Parameter

class TooManyArguments(UserInputError):
    """Exception when too many arguments are provided."""

class BadArgument(UserInputError):
    """Exception for invalid argument values or types."""

class BadUnionArgument(UserInputError):
    """Exception for failed union type conversions."""
    
    def __init__(self, param: Parameter, converters: List[Converter], errors: List[CommandError]):
        """
        Initialize bad union argument exception.
        
        Parameters:
        - param: Parameter that failed conversion
        - converters: Attempted converters
        - errors: Conversion errors
        """
    
    param: Parameter
    converters: List[Converter]
    errors: List[CommandError]

class BadLiteralArgument(UserInputError):
    """Exception for failed literal type conversions."""
    
    def __init__(self, param: Parameter, literals: List[Any], errors: List[CommandError]):
        """
        Initialize bad literal argument exception.
        
        Parameters:
        - param: Parameter that failed conversion
        - literals: Expected literal values
        - errors: Conversion errors
        """
    
    param: Parameter
    literals: List[Any]
    errors: List[CommandError]

class ArgumentParsingError(UserInputError):
    """Exception for general argument parsing failures."""

class UnexpectedQuoteError(ArgumentParsingError):
    """Exception for unexpected quotes in arguments."""
    
    def __init__(self, quote: str):
        """
        Initialize unexpected quote error.
        
        Parameters:
        - quote: The unexpected quote character
        """
    
    quote: str

class InvalidEndOfQuotedStringError(ArgumentParsingError):
    """Exception for invalid end of quoted string."""
    
    def __init__(self, char: str):
        """
        Initialize invalid end of quoted string error.
        
        Parameters:
        - char: The invalid character
        """
    
    char: str

class ExpectedClosingQuoteError(ArgumentParsingError):
    """Exception for missing closing quote."""
    
    def __init__(self, close_quote: str):
        """
        Initialize expected closing quote error.
        
        Parameters:
        - close_quote: Expected closing quote character
        """
    
    close_quote: str

class CheckFailure(CommandError):
    """Exception when command check fails."""

class CheckAnyFailure(CheckFailure):
    """Exception when all command checks fail."""
    
    def __init__(self, checks: List[Check], errors: List[CheckFailure]):
        """
        Initialize check any failure exception.
        
        Parameters:
        - checks: Failed checks
        - errors: Individual check errors
        """
    
    checks: List[Check]
    errors: List[CheckFailure]

class PrivateMessageOnly(CheckFailure):
    """Exception for DM-only commands used in guilds."""

class NoPrivateMessage(CheckFailure):
    """Exception for guild-only commands used in DMs."""

class NotOwner(CheckFailure):
    """Exception when non-owner tries to use owner-only command."""

class MissingRole(CheckFailure):
    """Exception for missing required role."""
    
    def __init__(self, missing_role: Union[str, int]):
        """
        Initialize missing role exception.
        
        Parameters:
        - missing_role: Required role name or ID
        """
    
    missing_role: Union[str, int]

class BotMissingRole(CheckFailure):
    """Exception when bot is missing required role."""
    
    def __init__(self, missing_role: Union[str, int]):
        """
        Initialize bot missing role exception.
        
        Parameters:
        - missing_role: Required role name or ID
        """
    
    missing_role: Union[str, int]

class MissingAnyRole(CheckFailure):
    """Exception when missing any of the required roles."""
    
    def __init__(self, missing_roles: List[Union[str, int]]):
        """
        Initialize missing any role exception.
        
        Parameters:
        - missing_roles: List of required role names or IDs
        """
    
    missing_roles: List[Union[str, int]]

class BotMissingAnyRole(CheckFailure):
    """Exception when bot is missing any of the required roles."""
    
    def __init__(self, missing_roles: List[Union[str, int]]):
        """
        Initialize bot missing any role exception.
        
        Parameters:
        - missing_roles: List of required role names or IDs
        """
    
    missing_roles: List[Union[str, int]]

class MissingPermissions(CheckFailure):
    """Exception for missing required permissions."""
    
    def __init__(self, missing_permissions: List[str]):
        """
        Initialize missing permissions exception.
        
        Parameters:
        - missing_permissions: List of missing permission names
        """
    
    missing_permissions: List[str]

class BotMissingPermissions(CheckFailure):
    """Exception when bot is missing required permissions."""
    
    def __init__(self, missing_permissions: List[str]):
        """
        Initialize bot missing permissions exception.
        
        Parameters:
        - missing_permissions: List of missing permission names
        """
    
    missing_permissions: List[str]

class NSFWChannelRequired(CheckFailure):
    """Exception for NSFW commands used in non-NSFW channels."""

class DisabledCommand(CommandError):
    """Exception when command is disabled."""

class CommandInvokeError(CommandError):
    """Exception wrapping errors during command execution."""
    
    def __init__(self, e: Exception):
        """
        Initialize command invoke error.
        
        Parameters:
        - e: Original exception that was raised
        """
    
    original: Exception

class CommandOnCooldown(CommandError):
    """Exception when command is on cooldown."""
    
    def __init__(self, cooldown: Cooldown, retry_after: float, type: BucketType):
        """
        Initialize command on cooldown exception.
        
        Parameters:
        - cooldown: Cooldown configuration
        - retry_after: Time until command can be used again
        - type: Cooldown bucket type
        """
    
    cooldown: Cooldown
    retry_after: float
    type: BucketType

class MaxConcurrencyReached(CommandError):
    """Exception when maximum command concurrency is reached."""
    
    def __init__(self, number: int, per: BucketType):
        """
        Initialize max concurrency exception.
        
        Parameters:
        - number: Maximum allowed concurrent uses
        - per: Concurrency bucket type
        """
    
    number: int
    per: BucketType

class UserInputError(CommandError):
    """Base exception for user input errors."""

class ConversionError(CommandError):
    """Exception for type conversion failures."""
    
    def __init__(self, converter: Converter, original: Exception):
        """
        Initialize conversion error.
        
        Parameters:
        - converter: Converter that failed
        - original: Original exception
        """
    
    converter: Converter
    original: Exception

class ExtensionError(DiscordException):
    """Base exception for extension-related errors."""
    
    def __init__(self, message: str = None, *args, name: str):
        """
        Initialize extension error.
        
        Parameters:
        - message: Error message
        - args: Additional arguments
        - name: Extension name
        """
    
    name: str

class ExtensionAlreadyLoaded(ExtensionError):
    """Exception when extension is already loaded."""

class ExtensionNotLoaded(ExtensionError):
    """Exception when extension is not loaded."""

class NoEntryPointError(ExtensionError):
    """Exception when extension has no setup function."""

class ExtensionFailed(ExtensionError):
    """Exception when extension fails to load."""
    
    def __init__(self, name: str, original: Exception):
        """
        Initialize extension failed exception.
        
        Parameters:
        - name: Extension name
        - original: Original exception
        """
    
    original: Exception

class ExtensionNotFound(ExtensionError):
    """Exception when extension module is not found."""

Application Command Exceptions

Errors specific to slash commands and application commands.

class ApplicationCommandError(DiscordException):
    """Base exception for application command errors."""

class ApplicationCommandInvokeError(ApplicationCommandError):
    """Exception wrapping errors during application command execution."""
    
    def __init__(self, e: Exception):
        """
        Initialize application command invoke error.
        
        Parameters:
        - e: Original exception that was raised
        """
    
    original: Exception

class ApplicationCommandOnCooldown(ApplicationCommandError):
    """Exception when application command is on cooldown."""
    
    def __init__(self, cooldown: Cooldown, retry_after: float, type: BucketType):
        """
        Initialize application command cooldown exception.
        
        Parameters:
        - cooldown: Cooldown configuration
        - retry_after: Time until command can be used again
        - type: Cooldown bucket type
        """
    
    cooldown: Cooldown
    retry_after: float
    type: BucketType

class ApplicationCommandCheckFailure(ApplicationCommandError):
    """Exception when application command check fails."""

Error Event Handlers

Event system for handling and responding to various error conditions.

@bot.event
async def on_error(event: str, *args, **kwargs):
    """
    Called when an error occurs in event handlers.
    
    Parameters:
    - event: Event name that caused the error
    - args: Event arguments
    - kwargs: Event keyword arguments
    """

@bot.event
async def on_command_error(ctx: Context, error: CommandError):
    """
    Called when command execution raises an error.
    
    Parameters:
    - ctx: Command context
    - error: Exception that was raised
    """

@bot.event
async def on_application_command_error(inter: ApplicationCommandInteraction, error: Exception):
    """
    Called when application command execution raises an error.
    
    Parameters:
    - inter: Application command interaction
    - error: Exception that was raised
    """

async def on_slash_command_error(inter: ApplicationCommandInteraction, error: Exception):
    """
    Called when slash command execution raises an error.
    
    Parameters:
    - inter: Slash command interaction
    - error: Exception that was raised
    """

async def on_user_command_error(inter: ApplicationCommandInteraction, error: Exception):
    """
    Called when user command execution raises an error.
    
    Parameters:
    - inter: User command interaction
    - error: Exception that was raised
    """

async def on_message_command_error(inter: ApplicationCommandInteraction, error: Exception):
    """
    Called when message command execution raises an error.
    
    Parameters:
    - inter: Message command interaction
    - error: Exception that was raised
    """

Error Recovery and Logging

Utilities for error recovery, logging, and debugging assistance.

class ErrorHandler:
    """Error handling and recovery utilities."""
    
    def __init__(self, bot: Bot):
        self.bot = bot
        self.error_log = []
        self.error_counts = defaultdict(int)
    
    def log_error(self, error: Exception, context: str = None):
        """
        Log error with context information.
        
        Parameters:
        - error: Exception to log
        - context: Additional context information
        """
    
    def get_error_embed(self, error: Exception, ctx: Context = None) -> Embed:
        """
        Create error embed for user display.
        
        Parameters:
        - error: Exception to format
        - ctx: Command context if available
        
        Returns:
        Formatted error embed
        """
    
    async def handle_http_error(self, error: HTTPException, ctx: Context = None) -> bool:
        """
        Handle HTTP errors with appropriate user feedback.
        
        Parameters:
        - error: HTTP exception
        - ctx: Command context if available
        
        Returns:
        True if error was handled
        """
    
    async def handle_permission_error(self, error: CheckFailure, ctx: Context) -> bool:
        """
        Handle permission-related errors.
        
        Parameters:
        - error: Permission check failure
        - ctx: Command context
        
        Returns:
        True if error was handled
        """
    
    async def handle_cooldown_error(self, error: CommandOnCooldown, ctx: Context) -> bool:
        """
        Handle cooldown errors with retry information.
        
        Parameters:
        - error: Cooldown exception
        - ctx: Command context
        
        Returns:
        True if error was handled
        """
    
    async def handle_argument_error(self, error: UserInputError, ctx: Context) -> bool:
        """
        Handle argument parsing and conversion errors.
        
        Parameters:
        - error: User input error
        - ctx: Command context
        
        Returns:
        True if error was handled
        """

def format_traceback(error: Exception) -> str:
    """
    Format exception traceback for logging.
    
    Parameters:
    - error: Exception to format
    
    Returns:
    Formatted traceback string
    """

def get_error_cause(error: Exception) -> str:
    """
    Get human-readable error cause.
    
    Parameters:
    - error: Exception to analyze
    
    Returns:
    Error cause description
    """

async def safe_send(channel: Messageable, content: str = None, **kwargs) -> Optional[Message]:
    """
    Safely send message with error handling.
    
    Parameters:
    - channel: Channel to send to
    - content: Message content
    - kwargs: Additional send parameters
    
    Returns:
    Sent message if successful, None otherwise
    """

def retry_on_error(*exceptions: Type[Exception], max_retries: int = 3, delay: float = 1.0):
    """
    Decorator for retrying operations on specific errors.
    
    Parameters:
    - exceptions: Exception types to retry on
    - max_retries: Maximum number of retry attempts
    - delay: Delay between retries in seconds
    
    Returns:
    Decorated function with retry logic
    """

async def with_timeout(coro: Awaitable, timeout: float, default: Any = None) -> Any:
    """
    Execute coroutine with timeout protection.
    
    Parameters:
    - coro: Coroutine to execute
    - timeout: Timeout in seconds
    - default: Default value on timeout
    
    Returns:
    Coroutine result or default value
    """

Usage Examples

Basic Error Handling Setup

import disnake
from disnake.ext import commands
import traceback
import logging

# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('discord_bot')

bot = commands.Bot(command_prefix='!', intents=disnake.Intents.all())

@bot.event
async def on_ready():
    print(f'Bot ready: {bot.user}')

@bot.event
async def on_error(event, *args, **kwargs):
    """Handle errors in event handlers."""
    logger.error(f'Error in event {event}:', exc_info=True)
    
    # Log to error channel if available
    error_channel = bot.get_channel(ERROR_CHANNEL_ID)  # Replace with actual channel ID
    if error_channel:
        embed = disnake.Embed(
            title=f"Error in {event}",
            description=f"```py\n{traceback.format_exc()[:1900]}\n```",
            color=0xff0000,
            timestamp=disnake.utils.utcnow()
        )
        try:
            await error_channel.send(embed=embed)
        except:
            pass  # Avoid error loops

@bot.event
async def on_command_error(ctx, error):
    """Comprehensive command error handling."""
    
    # Ignore these errors
    if isinstance(error, (commands.CommandNotFound, commands.DisabledCommand)):
        return
    
    # Extract original error from CommandInvokeError
    if isinstance(error, commands.CommandInvokeError):
        error = error.original
    
    # User input errors
    elif isinstance(error, commands.MissingRequiredArgument):
        embed = disnake.Embed(
            title="Missing Argument",
            description=f"You're missing the `{error.param.name}` parameter.",
            color=0xff9900
        )
        embed.add_field(name="Usage", value=f"`{ctx.prefix}{ctx.command.qualified_name} {ctx.command.signature}`")
        await ctx.send(embed=embed)
        return
    
    elif isinstance(error, commands.BadArgument):
        embed = disnake.Embed(
            title="Invalid Argument",
            description=f"Invalid argument provided: {error}",
            color=0xff9900
        )
        embed.add_field(name="Usage", value=f"`{ctx.prefix}{ctx.command.qualified_name} {ctx.command.signature}`")
        await ctx.send(embed=embed)
        return
    
    elif isinstance(error, commands.TooManyArguments):
        embed = disnake.Embed(
            title="Too Many Arguments",
            description="You provided too many arguments for this command.",
            color=0xff9900
        )
        embed.add_field(name="Usage", value=f"`{ctx.prefix}{ctx.command.qualified_name} {ctx.command.signature}`")
        await ctx.send(embed=embed)
        return
    
    # Permission errors
    elif isinstance(error, commands.MissingPermissions):
        missing = ', '.join(error.missing_permissions)
        embed = disnake.Embed(
            title="Missing Permissions",
            description=f"You need the following permissions: **{missing}**",
            color=0xff0000
        )
        await ctx.send(embed=embed)
        return
    
    elif isinstance(error, commands.BotMissingPermissions):
        missing = ', '.join(error.missing_permissions)
        embed = disnake.Embed(
            title="Bot Missing Permissions",
            description=f"I need the following permissions: **{missing}**",
            color=0xff0000
        )
        await ctx.send(embed=embed)
        return
    
    elif isinstance(error, commands.MissingRole):
        embed = disnake.Embed(
            title="Missing Role",
            description=f"You need the **{error.missing_role}** role to use this command.",
            color=0xff0000
        )
        await ctx.send(embed=embed)
        return
    
    elif isinstance(error, commands.MissingAnyRole):
        roles = ', '.join(str(role) for role in error.missing_roles)
        embed = disnake.Embed(
            title="Missing Role",
            description=f"You need one of these roles: **{roles}**",
            color=0xff0000
        )
        await ctx.send(embed=embed)
        return
    
    elif isinstance(error, commands.NotOwner):
        embed = disnake.Embed(
            title="Owner Only",
            description="Only the bot owner can use this command.",
            color=0xff0000
        )
        await ctx.send(embed=embed)
        return
    
    elif isinstance(error, commands.NoPrivateMessage):
        embed = disnake.Embed(
            title="Guild Only",
            description="This command cannot be used in private messages.",
            color=0xff0000
        )
        await ctx.send(embed=embed)
        return
    
    elif isinstance(error, commands.PrivateMessageOnly):
        embed = disnake.Embed(
            title="DM Only",
            description="This command can only be used in private messages.",
            color=0xff0000
        )
        await ctx.send(embed=embed)
        return
    
    elif isinstance(error, commands.NSFWChannelRequired):
        embed = disnake.Embed(
            title="NSFW Channel Required",
            description="This command can only be used in NSFW channels.",
            color=0xff0000
        )
        await ctx.send(embed=embed)
        return
    
    # Cooldown errors
    elif isinstance(error, commands.CommandOnCooldown):
        embed = disnake.Embed(
            title="Command on Cooldown",
            description=f"Try again in **{error.retry_after:.2f}** seconds.",
            color=0xff9900
        )
        await ctx.send(embed=embed, delete_after=error.retry_after)
        return
    
    elif isinstance(error, commands.MaxConcurrencyReached):
        embed = disnake.Embed(
            title="Max Concurrency Reached",
            description="This command is already being used by too many people. Try again later.",
            color=0xff9900
        )
        await ctx.send(embed=embed)
        return
    
    # HTTP errors
    elif isinstance(error, disnake.Forbidden):
        embed = disnake.Embed(
            title="Forbidden",
            description="I don't have permission to perform this action.",
            color=0xff0000
        )
        await ctx.send(embed=embed)
        return
    
    elif isinstance(error, disnake.NotFound):
        embed = disnake.Embed(
            title="Not Found",
            description="The requested resource could not be found.",
            color=0xff0000
        )
        await ctx.send(embed=embed)
        return
    
    elif isinstance(error, disnake.HTTPException):
        embed = disnake.Embed(
            title="HTTP Error",
            description=f"An HTTP error occurred: {error.text}",
            color=0xff0000
        )
        await ctx.send(embed=embed)
        return
    
    # Unknown error
    else:
        embed = disnake.Embed(
            title="Unexpected Error",
            description="An unexpected error occurred. The developers have been notified.",
            color=0xff0000
        )
        await ctx.send(embed=embed)
        
        # Log the error
        logger.error(f'Unexpected error in command {ctx.command}:', exc_info=error)
        
        # Send to error channel
        error_channel = bot.get_channel(ERROR_CHANNEL_ID)
        if error_channel:
            error_embed = disnake.Embed(
                title=f"Command Error: {ctx.command.qualified_name}",
                color=0xff0000,
                timestamp=disnake.utils.utcnow()
            )
            error_embed.add_field(name="User", value=f"{ctx.author} ({ctx.author.id})", inline=True)
            error_embed.add_field(name="Guild", value=f"{ctx.guild} ({ctx.guild.id})" if ctx.guild else "DM", inline=True)
            error_embed.add_field(name="Channel", value=f"#{ctx.channel} ({ctx.channel.id})", inline=True)
            error_embed.add_field(name="Command", value=f"`{ctx.message.content}`", inline=False)
            error_embed.add_field(
                name="Error",
                value=f"```py\n{traceback.format_exception(type(error), error, error.__traceback__)[-1][:1000]}\n```",
                inline=False
            )
            
            try:
                await error_channel.send(embed=error_embed)
            except:
                pass

bot.run('YOUR_BOT_TOKEN')

Application Command Error Handling

@bot.event
async def on_application_command_error(inter, error):
    """Handle application command errors."""
    
    # Extract original error
    if isinstance(error, disnake.ApplicationCommandInvokeError):
        error = error.original
    
    # Create base embed
    embed = disnake.Embed(color=0xff0000, timestamp=disnake.utils.utcnow())
    
    # Handle specific errors
    if isinstance(error, commands.MissingPermissions):
        missing = ', '.join(error.missing_permissions)
        embed.title = "Missing Permissions"
        embed.description = f"You need: **{missing}**"
    
    elif isinstance(error, commands.BotMissingPermissions):
        missing = ', '.join(error.missing_permissions)
        embed.title = "Bot Missing Permissions"
        embed.description = f"I need: **{missing}**"
    
    elif isinstance(error, commands.MissingRole):
        embed.title = "Missing Role"
        embed.description = f"You need the **{error.missing_role}** role."
    
    elif isinstance(error, commands.MissingAnyRole):
        roles = ', '.join(str(role) for role in error.missing_roles)
        embed.title = "Missing Role"
        embed.description = f"You need one of: **{roles}**"
    
    elif isinstance(error, commands.NotOwner):
        embed.title = "Owner Only"
        embed.description = "Only the bot owner can use this command."
    
    elif isinstance(error, commands.NoPrivateMessage):
        embed.title = "Guild Only"
        embed.description = "This command cannot be used in DMs."
    
    elif isinstance(error, commands.CommandOnCooldown):
        embed.title = "Command on Cooldown"
        embed.description = f"Try again in **{error.retry_after:.2f}** seconds."
        embed.color = 0xff9900
    
    elif isinstance(error, disnake.Forbidden):
        embed.title = "Forbidden"
        embed.description = "I don't have permission to perform this action."
    
    elif isinstance(error, disnake.NotFound):
        embed.title = "Not Found"
        embed.description = "The requested resource was not found."
    
    elif isinstance(error, ValueError):
        embed.title = "Invalid Input"
        embed.description = "Please check your input and try again."
        embed.color = 0xff9900
    
    else:
        embed.title = "Unexpected Error"
        embed.description = "An unexpected error occurred."
        
        # Log unexpected errors
        logger.error(f'Unexpected slash command error:', exc_info=error)
    
    # Send error response
    try:
        if inter.response.is_done():
            await inter.followup.send(embed=embed, ephemeral=True)
        else:
            await inter.response.send_message(embed=embed, ephemeral=True)
    except:
        # If we can't send the error message, log it
        logger.error(f'Failed to send error response for interaction {inter.id}')

# Command-specific error handlers
@bot.slash_command()
async def divide(inter, a: float, b: float):
    """Divide two numbers."""
    try:
        result = a / b
        await inter.response.send_message(f"{a} ÷ {b} = {result}")
    except ZeroDivisionError:
        embed = disnake.Embed(
            title="Math Error",
            description="Cannot divide by zero!",
            color=0xff0000
        )
        await inter.response.send_message(embed=embed, ephemeral=True)

@bot.slash_command()
async def risky_operation(inter):
    """Command that might fail."""
    try:
        # Potentially risky operation
        result = await some_api_call()
        await inter.response.send_message(f"Result: {result}")
    
    except aiohttp.ClientError as e:
        embed = disnake.Embed(
            title="Network Error",
            description="Failed to connect to external service. Try again later.",
            color=0xff9900
        )
        await inter.response.send_message(embed=embed, ephemeral=True)
        logger.warning(f'API call failed: {e}')
    
    except asyncio.TimeoutError:
        embed = disnake.Embed(
            title="Timeout",
            description="The operation timed out. Try again later.",
            color=0xff9900
        )
        await inter.response.send_message(embed=embed, ephemeral=True)
    
    except Exception as e:
        embed = disnake.Embed(
            title="Error",
            description="An unexpected error occurred.",
            color=0xff0000
        )
        await inter.response.send_message(embed=embed, ephemeral=True)
        logger.error(f'Unexpected error in risky_operation: {e}', exc_info=True)

Advanced Error Recovery System

import asyncio
import functools
from typing import Optional, Callable, Any
from datetime import datetime, timedelta

class ErrorHandler:
    """Advanced error handling and recovery system."""
    
    def __init__(self, bot):
        self.bot = bot
        self.error_counts = {}
        self.last_errors = {}
        self.recovery_strategies = {}
    
    def register_recovery_strategy(self, error_type: type, strategy: Callable):
        """Register a recovery strategy for specific error types."""
        self.recovery_strategies[error_type] = strategy
    
    async def handle_error(self, error: Exception, context: dict = None) -> bool:
        """
        Handle error with recovery strategies.
        
        Returns True if error was handled and recovered from.
        """
        error_type = type(error)
        error_key = f"{error_type.__name__}:{str(error)}"
        
        # Track error frequency
        now = datetime.utcnow()
        if error_key not in self.error_counts:
            self.error_counts[error_key] = []
        
        self.error_counts[error_key].append(now)
        
        # Clean old entries (last hour)
        cutoff = now - timedelta(hours=1)
        self.error_counts[error_key] = [
            t for t in self.error_counts[error_key] if t > cutoff
        ]
        
        # Check if error is recurring
        recent_count = len(self.error_counts[error_key])
        if recent_count > 5:
            logger.warning(f'Recurring error detected: {error_key} ({recent_count} times)')
        
        # Try recovery strategy
        if error_type in self.recovery_strategies:
            try:
                return await self.recovery_strategies[error_type](error, context)
            except Exception as recovery_error:
                logger.error(f'Recovery strategy failed: {recovery_error}')
        
        return False

# Initialize error handler
error_handler = ErrorHandler(bot)

# Recovery strategies
async def recover_from_forbidden(error: disnake.Forbidden, context: dict) -> bool:
    """Recovery strategy for Forbidden errors."""
    if context and 'channel' in context:
        channel = context['channel']
        
        # Try to send error message to a different channel
        if hasattr(channel, 'guild') and channel.guild:
            # Find a channel we can send to
            for text_channel in channel.guild.text_channels:
                try:
                    perms = text_channel.permissions_for(channel.guild.me)
                    if perms.send_messages:
                        await text_channel.send(
                            f"⚠️ I don't have permission to perform an action in {channel.mention}. "
                            f"Please check my permissions."
                        )
                        return True
                except:
                    continue
    
    return False

async def recover_from_not_found(error: disnake.NotFound, context: dict) -> bool:
    """Recovery strategy for NotFound errors."""
    if context and 'interaction' in context:
        inter = context['interaction']
        
        # If interaction is not found, it might have expired
        try:
            if not inter.response.is_done():
                await inter.response.send_message(
                    "This interaction has expired. Please try the command again.",
                    ephemeral=True
                )
                return True
        except:
            pass
    
    return False

async def recover_from_rate_limit(error: disnake.HTTPException, context: dict) -> bool:
    """Recovery strategy for rate limits."""
    if error.status == 429:  # Rate limited
        retry_after = getattr(error, 'retry_after', 1)
        logger.info(f'Rate limited, waiting {retry_after} seconds')
        
        await asyncio.sleep(retry_after)
        
        # Try to notify about the delay
        if context and 'ctx' in context:
            ctx = context['ctx']
            try:
                await ctx.send(
                    f"⏳ Rate limited. Retrying in {retry_after} seconds...",
                    delete_after=retry_after + 5
                )
            except:
                pass
        
        return True
    
    return False

# Register recovery strategies
error_handler.register_recovery_strategy(disnake.Forbidden, recover_from_forbidden)
error_handler.register_recovery_strategy(disnake.NotFound, recover_from_not_found)
error_handler.register_recovery_strategy(disnake.HTTPException, recover_from_rate_limit)

def with_error_recovery(func):
    """Decorator to add error recovery to functions."""
    @functools.wraps(func)
    async def wrapper(*args, **kwargs):
        try:
            return await func(*args, **kwargs)
        except Exception as e:
            context = {
                'function': func.__name__,
                'args': args,
                'kwargs': kwargs
            }
            
            # Try to extract useful context
            for arg in args:
                if isinstance(arg, (commands.Context, disnake.ApplicationCommandInteraction)):
                    context['ctx'] = arg
                    if hasattr(arg, 'channel'):
                        context['channel'] = arg.channel
                    if hasattr(arg, 'guild'):
                        context['guild'] = arg.guild
                    break
            
            # Attempt recovery
            recovered = await error_handler.handle_error(e, context)
            
            if not recovered:
                # Re-raise if recovery failed
                raise
    
    return wrapper

# Retry decorator
def retry_on_error(*error_types, max_retries=3, delay=1.0, backoff=2.0):
    """Decorator to retry function calls on specific errors."""
    def decorator(func):
        @functools.wraps(func)
        async def wrapper(*args, **kwargs):
            last_exception = None
            
            for attempt in range(max_retries + 1):
                try:
                    return await func(*args, **kwargs)
                except error_types as e:
                    last_exception = e
                    
                    if attempt == max_retries:
                        break
                    
                    wait_time = delay * (backoff ** attempt)
                    logger.info(f'Retrying {func.__name__} in {wait_time}s (attempt {attempt + 1}/{max_retries})')
                    await asyncio.sleep(wait_time)
            
            # All retries failed
            raise last_exception
        
        return wrapper
    return decorator

# Usage examples
@bot.command()
@with_error_recovery
@retry_on_error(disnake.HTTPException, max_retries=3, delay=2.0)
async def reliable_command(ctx):
    """A command with error recovery and retry logic."""
    # This might fail with HTTPException
    await ctx.send("This command has error recovery!")

@bot.slash_command()
@with_error_recovery
async def fetch_data(inter, url: str):
    """Fetch data from URL with error handling."""
    try:
        async with aiohttp.ClientSession() as session:
            async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as resp:
                if resp.status == 200:
                    data = await resp.text()
                    await inter.response.send_message(f"Data length: {len(data)} characters")
                else:
                    await inter.response.send_message(f"HTTP {resp.status}: {resp.reason}")
    
    except asyncio.TimeoutError:
        await inter.response.send_message("⏰ Request timed out", ephemeral=True)
    
    except aiohttp.ClientError as e:
        await inter.response.send_message(f"🌐 Network error: {e}", ephemeral=True)

# Safe execution context manager
class SafeExecution:
    """Context manager for safe code execution with error handling."""
    
    def __init__(self, ctx_or_inter, *, 
                 error_message="An error occurred",
                 log_errors=True,
                 reraise=False):
        self.ctx_or_inter = ctx_or_inter
        self.error_message = error_message
        self.log_errors = log_errors
        self.reraise = reraise
    
    async def __aenter__(self):
        return self
    
    async def __aexit__(self, exc_type, exc_val, exc_tb):
        if exc_type is not None:
            if self.log_errors:
                logger.error(f'Error in safe execution: {exc_val}', exc_info=exc_val)
            
            # Send error message
            try:
                if isinstance(self.ctx_or_inter, commands.Context):
                    await self.ctx_or_inter.send(self.error_message)
                else:
                    if self.ctx_or_inter.response.is_done():
                        await self.ctx_or_inter.followup.send(self.error_message, ephemeral=True)
                    else:
                        await self.ctx_or_inter.response.send_message(self.error_message, ephemeral=True)
            except:
                pass  # Avoid error loops
            
            # Suppress exception unless reraise is True
            return not self.reraise

# Usage of SafeExecution
@bot.command()
async def safe_command(ctx):
    """Command using safe execution context manager."""
    async with SafeExecution(ctx, error_message="Failed to process your request"):
        # Potentially risky operation
        result = await some_risky_operation()
        await ctx.send(f"Result: {result}")

# Error monitoring and alerts
class ErrorMonitor:
    """Monitor errors and send alerts when thresholds are exceeded."""
    
    def __init__(self, bot, alert_channel_id: int, threshold: int = 10):
        self.bot = bot
        self.alert_channel_id = alert_channel_id
        self.threshold = threshold
        self.error_window = timedelta(minutes=15)
        self.recent_errors = []
    
    def record_error(self, error: Exception, context: str = None):
        """Record an error occurrence."""
        now = datetime.utcnow()
        self.recent_errors.append({
            'timestamp': now,
            'error': str(error),
            'type': type(error).__name__,
            'context': context
        })
        
        # Clean old errors
        cutoff = now - self.error_window
        self.recent_errors = [e for e in self.recent_errors if e['timestamp'] > cutoff]
        
        # Check if we should send an alert
        if len(self.recent_errors) >= self.threshold:
            asyncio.create_task(self._send_alert())
    
    async def _send_alert(self):
        """Send error alert to monitoring channel."""
        channel = self.bot.get_channel(self.alert_channel_id)
        if not channel:
            return
        
        error_types = {}
        for error in self.recent_errors:
            error_type = error['type']
            error_types[error_type] = error_types.get(error_type, 0) + 1
        
        embed = disnake.Embed(
            title="🚨 Error Alert",
            description=f"**{len(self.recent_errors)}** errors in the last 15 minutes",
            color=0xff0000,
            timestamp=disnake.utils.utcnow()
        )
        
        error_summary = '\n'.join([f"**{error_type}**: {count}" for error_type, count in error_types.items()])
        embed.add_field(name="Error Types", value=error_summary, inline=False)
        
        try:
            await channel.send(embed=embed)
        except:
            pass
        
        # Clear recent errors after alert
        self.recent_errors.clear()

# Initialize error monitor
error_monitor = ErrorMonitor(bot, ERROR_ALERT_CHANNEL_ID)

# Update error handlers to use monitor
@bot.event
async def on_command_error(ctx, error):
    """Enhanced command error handler with monitoring."""
    error_monitor.record_error(error, f'Command: {ctx.command}')
    
    # Original error handling code here...
    # [Previous error handling implementation]

@bot.event
async def on_application_command_error(inter, error):
    """Enhanced application command error handler with monitoring."""
    error_monitor.record_error(error, f'Slash Command: {inter.data.name}')
    
    # Original error handling code here...
    # [Previous error handling implementation]

Install with Tessl CLI

npx tessl i tessl/pypi-disnake

docs

application-commands.md

automod.md

channels-messaging.md

client-bot.md

command-framework.md

error-handling.md

events-gateway.md

guild-management.md

index.md

interactions-ui.md

localization-i18n.md

permissions-security.md

polls.md

users-members.md

voice-audio.md

tile.json