0
# Nextcord Errors
1
2
Exception hierarchy and error handling patterns for robust Discord bot development with comprehensive error management capabilities.
3
4
## Exception Hierarchy
5
6
Core exception classes and their inheritance structure for handling various Discord API errors.
7
8
### Base Exceptions { .api }
9
10
```python
11
import nextcord
12
from nextcord import DiscordException, ClientException, HTTPException
13
from typing import Optional, Dict, Any, Union, List
14
import aiohttp
15
16
class DiscordException(Exception):
17
"""Base exception class for all nextcord exceptions.
18
19
This is the root exception that all other nextcord exceptions inherit from.
20
Catching this will catch all nextcord-specific errors.
21
"""
22
pass
23
24
class ClientException(DiscordException):
25
"""Exception raised when an operation in the Client fails.
26
27
These are usually user errors, such as providing invalid parameters
28
or attempting operations that are not allowed.
29
"""
30
pass
31
32
class HTTPException(DiscordException):
33
"""Exception raised when an HTTP request operation fails.
34
35
Attributes
36
----------
37
response: aiohttp.ClientResponse
38
The aiohttp response object.
39
status: int
40
The HTTP status code.
41
code: int
42
The Discord error code.
43
text: str
44
The error message from Discord.
45
"""
46
47
def __init__(self, response: aiohttp.ClientResponse, message: Optional[str]):
48
self.response = response
49
self.status = response.status
50
self.code = 0
51
self.text = message or ''
52
53
if message:
54
super().__init__(f'{self.status} {self.response.reason} (error code: {self.code}): {self.text}')
55
else:
56
super().__init__(f'{self.status} {self.response.reason}')
57
58
# Basic error handling example
59
@bot.event
60
async def on_command_error(ctx, error):
61
"""Global error handler for command errors."""
62
63
if isinstance(error, nextcord.HTTPException):
64
if error.status == 403:
65
await ctx.send("❌ I don't have permission to do that!")
66
elif error.status == 404:
67
await ctx.send("❌ That resource was not found.")
68
else:
69
await ctx.send(f"❌ An HTTP error occurred: {error.text}")
70
71
elif isinstance(error, nextcord.ClientException):
72
await ctx.send(f"❌ Client error: {str(error)}")
73
74
else:
75
print(f"Unexpected error: {type(error).__name__}: {error}")
76
await ctx.send("❌ An unexpected error occurred.")
77
78
# Specific exception handling
79
try:
80
await channel.send("Hello!")
81
except nextcord.Forbidden:
82
print("No permission to send messages to this channel")
83
except nextcord.HTTPException as e:
84
print(f"HTTP error: {e.status} - {e.text}")
85
except nextcord.DiscordException:
86
print("A Discord-related error occurred")
87
```
88
89
## HTTP and API Errors
90
91
Specific HTTP exceptions for different Discord API error conditions.
92
93
### HTTP Status Exceptions { .api }
94
95
```python
96
class Forbidden(HTTPException):
97
"""Exception raised for 403 Forbidden HTTP responses.
98
99
This typically means the bot lacks the necessary permissions
100
to perform the requested action.
101
"""
102
pass
103
104
class NotFound(HTTPException):
105
"""Exception raised for 404 Not Found HTTP responses.
106
107
This means the requested resource (user, channel, message, etc.)
108
does not exist or is not accessible.
109
"""
110
pass
111
112
class DiscordServerError(HTTPException):
113
"""Exception raised for 5xx HTTP responses.
114
115
These are server-side errors from Discord's API and are
116
usually temporary.
117
"""
118
pass
119
120
class RateLimited(HTTPException):
121
"""Exception raised when being rate limited by Discord.
122
123
Attributes
124
----------
125
retry_after: float
126
Number of seconds to wait before retrying.
127
"""
128
129
def __init__(self, response: aiohttp.ClientResponse, message: str, retry_after: float):
130
super().__init__(response, message)
131
self.retry_after = retry_after
132
133
# Comprehensive error handling
134
async def safe_send_message(channel, content, **kwargs):
135
"""Safely send a message with comprehensive error handling."""
136
try:
137
return await channel.send(content, **kwargs)
138
139
except nextcord.Forbidden:
140
print(f"No permission to send messages in {channel}")
141
return None
142
143
except nextcord.NotFound:
144
print(f"Channel {channel.id} not found or deleted")
145
return None
146
147
except nextcord.RateLimited as e:
148
print(f"Rate limited! Retry after {e.retry_after} seconds")
149
await asyncio.sleep(e.retry_after)
150
return await channel.send(content, **kwargs) # Retry once
151
152
except nextcord.HTTPException as e:
153
print(f"HTTP error {e.status}: {e.text}")
154
return None
155
156
except Exception as e:
157
print(f"Unexpected error: {type(e).__name__}: {e}")
158
return None
159
160
# Guild-specific error handling
161
async def safe_add_role(member, role, reason=None):
162
"""Safely add a role to a member."""
163
try:
164
await member.add_roles(role, reason=reason)
165
return True
166
167
except nextcord.Forbidden:
168
print(f"No permission to manage roles for {member}")
169
return False
170
171
except nextcord.NotFound:
172
print(f"Member {member} or role {role} not found")
173
return False
174
175
except nextcord.HTTPException as e:
176
if e.code == 50013: # Missing permissions
177
print(f"Missing permissions to add role {role.name}")
178
elif e.code == 50025: # Invalid OAuth2 access token
179
print("Invalid bot token")
180
else:
181
print(f"Failed to add role: {e.text}")
182
return False
183
184
# Message-related error handling
185
async def safe_edit_message(message, **kwargs):
186
"""Safely edit a message with error handling."""
187
try:
188
return await message.edit(**kwargs)
189
190
except nextcord.Forbidden:
191
print("No permission to edit this message")
192
return None
193
194
except nextcord.NotFound:
195
print("Message not found (may have been deleted)")
196
return None
197
198
except nextcord.HTTPException as e:
199
if e.code == 50005: # Cannot edit message by another user
200
print("Cannot edit message from another user")
201
elif e.code == 50001: # Missing access
202
print("Missing access to edit message")
203
else:
204
print(f"Failed to edit message: {e.text}")
205
return None
206
207
# Voice-related error handling
208
async def safe_connect_voice(channel):
209
"""Safely connect to a voice channel."""
210
try:
211
return await channel.connect()
212
213
except nextcord.ClientException as e:
214
if "already connected" in str(e).lower():
215
print("Already connected to a voice channel")
216
# Get existing connection
217
return nextcord.utils.get(bot.voice_clients, guild=channel.guild)
218
else:
219
print(f"Client error connecting to voice: {e}")
220
return None
221
222
except nextcord.Forbidden:
223
print(f"No permission to connect to {channel.name}")
224
return None
225
226
except asyncio.TimeoutError:
227
print("Timeout connecting to voice channel")
228
return None
229
230
except Exception as e:
231
print(f"Unexpected voice connection error: {e}")
232
return None
233
```
234
235
## Command Framework Errors
236
237
Errors specific to the command framework and command processing.
238
239
### Command Exceptions { .api }
240
241
```python
242
from nextcord.ext import commands
243
244
class CommandError(DiscordException):
245
"""Base exception for all command related errors.
246
247
This inherits from DiscordException and is the base for all
248
command framework exceptions.
249
250
Attributes
251
----------
252
message: Optional[str]
253
The error message.
254
"""
255
256
def __init__(self, message: Optional[str] = None, *args):
257
if message is not None:
258
super().__init__(message, *args)
259
else:
260
super().__init__(*args)
261
262
class CommandNotFound(CommandError):
263
"""Exception raised when a command is not found.
264
265
This is raised when a user tries to invoke a command
266
that doesn't exist.
267
"""
268
pass
269
270
class MissingRequiredArgument(CommandError):
271
"""Exception raised when a required argument is missing.
272
273
Attributes
274
----------
275
param: inspect.Parameter
276
The parameter that was missing.
277
"""
278
279
def __init__(self, param):
280
self.param = param
281
super().__init__(f'{param.name} is a required argument that is missing.')
282
283
class BadArgument(CommandError):
284
"""Exception raised when parsing an argument fails.
285
286
This is typically raised when a converter fails to convert
287
the provided argument to the expected type.
288
"""
289
pass
290
291
class CheckFailure(CommandError):
292
"""Exception raised when a check fails.
293
294
This is raised when a command check (like permissions,
295
cooldowns, etc.) fails.
296
"""
297
pass
298
299
class MissingPermissions(CheckFailure):
300
"""Exception raised when the user lacks required permissions.
301
302
Attributes
303
----------
304
missing_perms: List[str]
305
List of missing permission names.
306
"""
307
308
def __init__(self, missing_perms: List[str], *args):
309
self.missing_perms = missing_perms
310
311
missing = [perm.replace('_', ' ').replace('guild', 'server').title() for perm in missing_perms]
312
313
if len(missing) > 2:
314
fmt = '{}, and {}'.format(", ".join(missing[:-1]), missing[-1])
315
else:
316
fmt = ' and '.join(missing)
317
318
message = f'You are missing {fmt} permission(s) to run this command.'
319
super().__init__(message, *args)
320
321
class BotMissingPermissions(CheckFailure):
322
"""Exception raised when the bot lacks required permissions.
323
324
Attributes
325
----------
326
missing_perms: List[str]
327
List of missing permission names.
328
"""
329
330
def __init__(self, missing_perms: List[str], *args):
331
self.missing_perms = missing_perms
332
333
missing = [perm.replace('_', ' ').replace('guild', 'server').title() for perm in missing_perms]
334
335
if len(missing) > 2:
336
fmt = '{}, and {}'.format(", ".join(missing[:-1]), missing[-1])
337
else:
338
fmt = ' and '.join(missing)
339
340
message = f'Bot requires {fmt} permission(s) to run this command.'
341
super().__init__(message, *args)
342
343
class CommandOnCooldown(CommandError):
344
"""Exception raised when a command is on cooldown.
345
346
Attributes
347
----------
348
cooldown: Cooldown
349
The cooldown that was triggered.
350
retry_after: float
351
Number of seconds until the command can be used again.
352
"""
353
354
def __init__(self, cooldown, retry_after: float):
355
self.cooldown = cooldown
356
self.retry_after = retry_after
357
super().__init__(f'You are on cooldown. Try again in {retry_after:.2f}s')
358
359
# Command error handling examples
360
@bot.event
361
async def on_command_error(ctx: commands.Context, error: commands.CommandError):
362
"""Global command error handler."""
363
364
# Ignore command not found errors
365
if isinstance(error, commands.CommandNotFound):
366
return
367
368
# Handle missing arguments
369
elif isinstance(error, commands.MissingRequiredArgument):
370
embed = nextcord.Embed(
371
title="❌ Missing Argument",
372
description=f"Missing required argument: `{error.param.name}`",
373
color=nextcord.Color.red()
374
)
375
embed.add_field(
376
name="Usage",
377
value=f"`{ctx.prefix}{ctx.command.qualified_name} {ctx.command.signature}`",
378
inline=False
379
)
380
await ctx.send(embed=embed)
381
382
# Handle bad arguments (conversion failures)
383
elif isinstance(error, commands.BadArgument):
384
embed = nextcord.Embed(
385
title="❌ Invalid Argument",
386
description=str(error),
387
color=nextcord.Color.red()
388
)
389
await ctx.send(embed=embed)
390
391
# Handle permission errors
392
elif isinstance(error, commands.MissingPermissions):
393
embed = nextcord.Embed(
394
title="❌ Missing Permissions",
395
description=str(error),
396
color=nextcord.Color.red()
397
)
398
await ctx.send(embed=embed)
399
400
elif isinstance(error, commands.BotMissingPermissions):
401
embed = nextcord.Embed(
402
title="❌ Bot Missing Permissions",
403
description=str(error),
404
color=nextcord.Color.red()
405
)
406
await ctx.send(embed=embed)
407
408
# Handle cooldowns
409
elif isinstance(error, commands.CommandOnCooldown):
410
embed = nextcord.Embed(
411
title="⏰ Command on Cooldown",
412
description=f"Please wait {error.retry_after:.1f} seconds before using this command again.",
413
color=nextcord.Color.orange()
414
)
415
await ctx.send(embed=embed, delete_after=error.retry_after)
416
417
# Handle other command errors
418
elif isinstance(error, commands.CommandError):
419
embed = nextcord.Embed(
420
title="❌ Command Error",
421
description=str(error),
422
color=nextcord.Color.red()
423
)
424
await ctx.send(embed=embed)
425
426
# Handle HTTP exceptions
427
elif isinstance(error, nextcord.HTTPException):
428
if error.status == 403:
429
embed = nextcord.Embed(
430
title="❌ Permission Denied",
431
description="I don't have permission to perform that action.",
432
color=nextcord.Color.red()
433
)
434
elif error.status == 404:
435
embed = nextcord.Embed(
436
title="❌ Not Found",
437
description="The requested resource was not found.",
438
color=nextcord.Color.red()
439
)
440
else:
441
embed = nextcord.Embed(
442
title="❌ HTTP Error",
443
description=f"An error occurred: {error.text}",
444
color=nextcord.Color.red()
445
)
446
await ctx.send(embed=embed)
447
448
# Handle unexpected errors
449
else:
450
embed = nextcord.Embed(
451
title="❌ Unexpected Error",
452
description="An unexpected error occurred. Please try again later.",
453
color=nextcord.Color.red()
454
)
455
await ctx.send(embed=embed)
456
457
# Log the error for debugging
458
print(f"Unexpected error in {ctx.command}: {type(error).__name__}: {error}")
459
460
# Command-specific error handling
461
@bot.command()
462
async def ban_user(ctx, member: nextcord.Member, *, reason: str = "No reason provided"):
463
"""Ban a user with proper error handling."""
464
try:
465
await member.ban(reason=reason)
466
467
embed = nextcord.Embed(
468
title="🔨 User Banned",
469
description=f"{member.mention} has been banned.",
470
color=nextcord.Color.red()
471
)
472
embed.add_field(name="Reason", value=reason, inline=False)
473
embed.set_footer(text=f"Banned by {ctx.author}", icon_url=ctx.author.display_avatar.url)
474
475
await ctx.send(embed=embed)
476
477
except nextcord.Forbidden:
478
await ctx.send("❌ I don't have permission to ban this user.")
479
except nextcord.HTTPException as e:
480
if e.code == 50013: # Missing permissions
481
await ctx.send("❌ I cannot ban this user (they may have higher permissions).")
482
else:
483
await ctx.send(f"❌ Failed to ban user: {e.text}")
484
485
# Converter error handling
486
class CustomMemberConverter(commands.Converter):
487
"""Custom member converter with detailed error messages."""
488
489
async def convert(self, ctx: commands.Context, argument: str) -> nextcord.Member:
490
try:
491
# Try default member conversion
492
return await commands.MemberConverter().convert(ctx, argument)
493
except commands.MemberNotFound:
494
# Provide helpful error message
495
raise commands.BadArgument(
496
f"Member '{argument}' not found. "
497
"Try using their full username, nickname, or mention."
498
)
499
500
@bot.command()
501
async def userinfo(ctx, member: CustomMemberConverter = None):
502
"""Get user info with custom converter error handling."""
503
member = member or ctx.author
504
505
embed = nextcord.Embed(title=f"User Info: {member}", color=member.color)
506
embed.set_thumbnail(url=member.display_avatar.url)
507
embed.add_field(name="ID", value=member.id, inline=True)
508
embed.add_field(name="Joined", value=member.joined_at.strftime("%Y-%m-%d"), inline=True)
509
510
await ctx.send(embed=embed)
511
```
512
513
## Application Command Errors
514
515
Errors specific to slash commands and application commands.
516
517
### Interaction Exceptions { .api }
518
519
```python
520
class ApplicationCommandError(DiscordException):
521
"""Base exception for application command errors."""
522
pass
523
524
class InteractionResponded(ClientException):
525
"""Exception raised when an interaction has already been responded to.
526
527
This occurs when you try to respond to an interaction that has
528
already received a response.
529
"""
530
pass
531
532
class InteractionNotResponded(ClientException):
533
"""Exception raised when an interaction hasn't been responded to.
534
535
This occurs when you try to edit or delete an interaction response
536
that hasn't been sent yet.
537
"""
538
pass
539
540
class InteractionTimedOut(ClientException):
541
"""Exception raised when an interaction times out.
542
543
Interactions must be responded to within 3 seconds, or they will
544
time out and become invalid.
545
"""
546
pass
547
548
# Application command error handling
549
@bot.event
550
async def on_application_command_error(interaction: nextcord.Interaction, error: Exception):
551
"""Global application command error handler."""
552
553
if isinstance(error, nextcord.ApplicationCommandError):
554
# Handle application command specific errors
555
await handle_app_command_error(interaction, error)
556
557
elif isinstance(error, nextcord.InteractionResponded):
558
print(f"Interaction {interaction.id} already responded to")
559
return # Can't send response since it's already responded
560
561
elif isinstance(error, nextcord.InteractionNotResponded):
562
try:
563
embed = nextcord.Embed(
564
title="❌ Command Error",
565
description="An error occurred while processing your command.",
566
color=nextcord.Color.red()
567
)
568
await interaction.response.send_message(embed=embed, ephemeral=True)
569
except:
570
print(f"Failed to respond to interaction {interaction.id}")
571
572
elif isinstance(error, nextcord.Forbidden):
573
try:
574
embed = nextcord.Embed(
575
title="❌ Permission Denied",
576
description="I don't have permission to perform that action.",
577
color=nextcord.Color.red()
578
)
579
580
if not interaction.response.is_done():
581
await interaction.response.send_message(embed=embed, ephemeral=True)
582
else:
583
await interaction.followup.send(embed=embed, ephemeral=True)
584
except:
585
pass
586
587
else:
588
# Log unexpected errors
589
print(f"Unexpected application command error: {type(error).__name__}: {error}")
590
591
try:
592
embed = nextcord.Embed(
593
title="❌ Unexpected Error",
594
description="An unexpected error occurred.",
595
color=nextcord.Color.red()
596
)
597
598
if not interaction.response.is_done():
599
await interaction.response.send_message(embed=embed, ephemeral=True)
600
else:
601
await interaction.followup.send(embed=embed, ephemeral=True)
602
except:
603
pass
604
605
async def handle_app_command_error(interaction: nextcord.Interaction, error: nextcord.ApplicationCommandError):
606
"""Handle application command specific errors."""
607
608
embed = nextcord.Embed(
609
title="❌ Command Error",
610
description=str(error),
611
color=nextcord.Color.red()
612
)
613
614
try:
615
if not interaction.response.is_done():
616
await interaction.response.send_message(embed=embed, ephemeral=True)
617
else:
618
await interaction.followup.send(embed=embed, ephemeral=True)
619
except nextcord.InteractionResponded:
620
# Already responded, try followup
621
try:
622
await interaction.followup.send(embed=embed, ephemeral=True)
623
except:
624
pass
625
626
# Safe interaction response helper
627
async def safe_interaction_response(
628
interaction: nextcord.Interaction,
629
content: str = None,
630
embed: nextcord.Embed = None,
631
ephemeral: bool = False,
632
**kwargs
633
):
634
"""Safely respond to an interaction with error handling."""
635
try:
636
if not interaction.response.is_done():
637
await interaction.response.send_message(
638
content=content,
639
embed=embed,
640
ephemeral=ephemeral,
641
**kwargs
642
)
643
else:
644
await interaction.followup.send(
645
content=content,
646
embed=embed,
647
ephemeral=ephemeral,
648
**kwargs
649
)
650
return True
651
652
except nextcord.InteractionResponded:
653
print(f"Interaction {interaction.id} already responded to")
654
return False
655
656
except nextcord.InteractionTimedOut:
657
print(f"Interaction {interaction.id} timed out")
658
return False
659
660
except nextcord.HTTPException as e:
661
print(f"HTTP error responding to interaction: {e.status} - {e.text}")
662
return False
663
664
except Exception as e:
665
print(f"Unexpected error responding to interaction: {e}")
666
return False
667
668
# Slash command with comprehensive error handling
669
@bot.slash_command(description="Kick a member from the server")
670
async def kick_member(
671
interaction: nextcord.Interaction,
672
member: nextcord.Member,
673
reason: str = "No reason provided"
674
):
675
"""Kick command with proper error handling."""
676
677
# Check permissions
678
if not interaction.user.guild_permissions.kick_members:
679
embed = nextcord.Embed(
680
title="❌ Missing Permissions",
681
description="You need 'Kick Members' permission to use this command.",
682
color=nextcord.Color.red()
683
)
684
await safe_interaction_response(interaction, embed=embed, ephemeral=True)
685
return
686
687
# Check if bot can kick the member
688
if member.top_role >= interaction.guild.me.top_role:
689
embed = nextcord.Embed(
690
title="❌ Cannot Kick",
691
description="I cannot kick this member (they have equal or higher role).",
692
color=nextcord.Color.red()
693
)
694
await safe_interaction_response(interaction, embed=embed, ephemeral=True)
695
return
696
697
# Check if user can kick the member
698
if member.top_role >= interaction.user.top_role and interaction.user != interaction.guild.owner:
699
embed = nextcord.Embed(
700
title="❌ Cannot Kick",
701
description="You cannot kick this member (they have equal or higher role).",
702
color=nextcord.Color.red()
703
)
704
await safe_interaction_response(interaction, embed=embed, ephemeral=True)
705
return
706
707
try:
708
# Attempt to kick the member
709
await member.kick(reason=f"{reason} | Kicked by {interaction.user}")
710
711
embed = nextcord.Embed(
712
title="👢 Member Kicked",
713
description=f"{member.mention} has been kicked from the server.",
714
color=nextcord.Color.orange()
715
)
716
embed.add_field(name="Reason", value=reason, inline=False)
717
embed.set_footer(text=f"Kicked by {interaction.user}", icon_url=interaction.user.display_avatar.url)
718
719
await safe_interaction_response(interaction, embed=embed)
720
721
except nextcord.Forbidden:
722
embed = nextcord.Embed(
723
title="❌ Permission Denied",
724
description="I don't have permission to kick this member.",
725
color=nextcord.Color.red()
726
)
727
await safe_interaction_response(interaction, embed=embed, ephemeral=True)
728
729
except nextcord.HTTPException as e:
730
embed = nextcord.Embed(
731
title="❌ Kick Failed",
732
description=f"Failed to kick member: {e.text}",
733
color=nextcord.Color.red()
734
)
735
await safe_interaction_response(interaction, embed=embed, ephemeral=True)
736
```
737
738
## Custom Error Classes
739
740
Creating custom exceptions for specific bot functionality and domain-specific errors.
741
742
### Custom Exception Implementation { .api }
743
744
```python
745
# Custom bot-specific exceptions
746
class BotException(DiscordException):
747
"""Base exception for custom bot errors."""
748
pass
749
750
class DatabaseError(BotException):
751
"""Exception raised when database operations fail.
752
753
Attributes
754
----------
755
operation: str
756
The database operation that failed.
757
table: Optional[str]
758
The table involved in the operation.
759
"""
760
761
def __init__(self, operation: str, table: Optional[str] = None, message: Optional[str] = None):
762
self.operation = operation
763
self.table = table
764
765
if message:
766
super().__init__(message)
767
else:
768
msg = f"Database {operation} failed"
769
if table:
770
msg += f" on table '{table}'"
771
super().__init__(msg)
772
773
class ConfigurationError(BotException):
774
"""Exception raised for configuration-related errors.
775
776
Attributes
777
----------
778
setting: str
779
The configuration setting that caused the error.
780
"""
781
782
def __init__(self, setting: str, message: Optional[str] = None):
783
self.setting = setting
784
super().__init__(message or f"Configuration error for setting '{setting}'")
785
786
class RateLimitExceeded(BotException):
787
"""Exception raised when custom rate limits are exceeded.
788
789
Attributes
790
----------
791
user_id: int
792
The user who exceeded the rate limit.
793
command: str
794
The command that was rate limited.
795
retry_after: float
796
Seconds until the user can retry.
797
"""
798
799
def __init__(self, user_id: int, command: str, retry_after: float):
800
self.user_id = user_id
801
self.command = command
802
self.retry_after = retry_after
803
super().__init__(f"Rate limit exceeded for {command}. Try again in {retry_after:.1f}s")
804
805
class InsufficientBalance(BotException):
806
"""Exception raised when a user has insufficient balance for an operation.
807
808
Attributes
809
----------
810
user_id: int
811
The user's ID.
812
required: int
813
The required amount.
814
available: int
815
The available amount.
816
"""
817
818
def __init__(self, user_id: int, required: int, available: int):
819
self.user_id = user_id
820
self.required = required
821
self.available = available
822
super().__init__(f"Insufficient balance. Required: {required}, Available: {available}")
823
824
class ItemNotFound(BotException):
825
"""Exception raised when a requested item is not found.
826
827
Attributes
828
----------
829
item_type: str
830
The type of item (e.g., 'user', 'guild', 'item').
831
item_id: Union[int, str]
832
The ID or name of the item.
833
"""
834
835
def __init__(self, item_type: str, item_id: Union[int, str]):
836
self.item_type = item_type
837
self.item_id = item_id
838
super().__init__(f"{item_type.title()} '{item_id}' not found")
839
840
# Custom error handler for bot-specific errors
841
class BotErrorHandler:
842
"""Centralized error handler for custom bot errors."""
843
844
def __init__(self, bot):
845
self.bot = bot
846
self.error_log_channel = None
847
848
def set_error_log_channel(self, channel_id: int):
849
"""Set the channel where errors should be logged."""
850
self.error_log_channel = channel_id
851
852
async def handle_error(
853
self,
854
error: Exception,
855
ctx: Optional[commands.Context] = None,
856
interaction: Optional[nextcord.Interaction] = None,
857
user: Optional[nextcord.User] = None
858
):
859
"""Handle different types of errors with appropriate responses."""
860
861
# Determine response method
862
if ctx:
863
respond = lambda **kwargs: ctx.send(**kwargs)
864
elif interaction:
865
respond = lambda **kwargs: safe_interaction_response(interaction, **kwargs)
866
else:
867
respond = None
868
869
# Handle custom errors
870
if isinstance(error, DatabaseError):
871
await self._handle_database_error(error, respond)
872
873
elif isinstance(error, ConfigurationError):
874
await self._handle_config_error(error, respond)
875
876
elif isinstance(error, RateLimitExceeded):
877
await self._handle_rate_limit_error(error, respond)
878
879
elif isinstance(error, InsufficientBalance):
880
await self._handle_balance_error(error, respond)
881
882
elif isinstance(error, ItemNotFound):
883
await self._handle_not_found_error(error, respond)
884
885
# Handle Discord errors
886
elif isinstance(error, nextcord.Forbidden):
887
await self._handle_forbidden_error(error, respond)
888
889
elif isinstance(error, nextcord.NotFound):
890
await self._handle_not_found_discord_error(error, respond)
891
892
else:
893
await self._handle_unexpected_error(error, respond, ctx, interaction)
894
895
async def _handle_database_error(self, error: DatabaseError, respond):
896
"""Handle database errors."""
897
embed = nextcord.Embed(
898
title="🗃️ Database Error",
899
description="A database error occurred. Please try again later.",
900
color=nextcord.Color.red()
901
)
902
903
if respond:
904
await respond(embed=embed)
905
906
# Log to error channel
907
await self._log_error("Database Error", str(error))
908
909
async def _handle_config_error(self, error: ConfigurationError, respond):
910
"""Handle configuration errors."""
911
embed = nextcord.Embed(
912
title="⚙️ Configuration Error",
913
description="A configuration issue was detected. Please contact an administrator.",
914
color=nextcord.Color.red()
915
)
916
917
if respond:
918
await respond(embed=embed)
919
920
await self._log_error("Configuration Error", f"Setting: {error.setting} - {str(error)}")
921
922
async def _handle_rate_limit_error(self, error: RateLimitExceeded, respond):
923
"""Handle custom rate limit errors."""
924
embed = nextcord.Embed(
925
title="⏰ Rate Limited",
926
description=f"You're using commands too quickly! Please wait {error.retry_after:.1f} seconds.",
927
color=nextcord.Color.orange()
928
)
929
930
if respond:
931
await respond(embed=embed)
932
933
async def _handle_balance_error(self, error: InsufficientBalance, respond):
934
"""Handle insufficient balance errors."""
935
embed = nextcord.Embed(
936
title="💰 Insufficient Balance",
937
description=f"You need {error.required} but only have {error.available}.",
938
color=nextcord.Color.red()
939
)
940
941
if respond:
942
await respond(embed=embed)
943
944
async def _handle_not_found_error(self, error: ItemNotFound, respond):
945
"""Handle item not found errors."""
946
embed = nextcord.Embed(
947
title="❓ Not Found",
948
description=f"{error.item_type.title()} '{error.item_id}' was not found.",
949
color=nextcord.Color.red()
950
)
951
952
if respond:
953
await respond(embed=embed)
954
955
async def _handle_forbidden_error(self, error: nextcord.Forbidden, respond):
956
"""Handle Discord forbidden errors."""
957
embed = nextcord.Embed(
958
title="❌ Permission Denied",
959
description="I don't have permission to perform that action.",
960
color=nextcord.Color.red()
961
)
962
963
if respond:
964
await respond(embed=embed)
965
966
async def _handle_not_found_discord_error(self, error: nextcord.NotFound, respond):
967
"""Handle Discord not found errors."""
968
embed = nextcord.Embed(
969
title="❓ Resource Not Found",
970
description="The requested Discord resource was not found.",
971
color=nextcord.Color.red()
972
)
973
974
if respond:
975
await respond(embed=embed)
976
977
async def _handle_unexpected_error(
978
self,
979
error: Exception,
980
respond,
981
ctx: Optional[commands.Context],
982
interaction: Optional[nextcord.Interaction]
983
):
984
"""Handle unexpected errors."""
985
embed = nextcord.Embed(
986
title="❌ Unexpected Error",
987
description="An unexpected error occurred. The developers have been notified.",
988
color=nextcord.Color.red()
989
)
990
991
if respond:
992
await respond(embed=embed)
993
994
# Log detailed error information
995
error_info = {
996
'type': type(error).__name__,
997
'message': str(error),
998
'command': ctx.command.name if ctx else None,
999
'interaction': interaction.data.get('name') if interaction else None,
1000
'user': (ctx.author.id if ctx else interaction.user.id if interaction else None),
1001
'guild': (ctx.guild.id if ctx and ctx.guild else interaction.guild.id if interaction and interaction.guild else None)
1002
}
1003
1004
await self._log_error("Unexpected Error", str(error_info))
1005
1006
async def _log_error(self, title: str, description: str):
1007
"""Log error to designated channel."""
1008
if not self.error_log_channel:
1009
return
1010
1011
channel = self.bot.get_channel(self.error_log_channel)
1012
if not channel:
1013
return
1014
1015
embed = nextcord.Embed(
1016
title=f"🚨 {title}",
1017
description=f"```\n{description}\n```",
1018
color=nextcord.Color.red(),
1019
timestamp=datetime.now()
1020
)
1021
1022
try:
1023
await channel.send(embed=embed)
1024
except:
1025
pass # Silently fail if we can't log
1026
1027
# Initialize error handler
1028
error_handler = BotErrorHandler(bot)
1029
error_handler.set_error_log_channel(ERROR_LOG_CHANNEL_ID)
1030
1031
# Usage in commands
1032
@bot.command()
1033
async def buy_item(ctx, item_name: str):
1034
"""Buy an item with comprehensive error handling."""
1035
try:
1036
# Get user data
1037
user_data = await get_user_data(ctx.author.id)
1038
if not user_data:
1039
raise ItemNotFound("user", ctx.author.id)
1040
1041
# Get item data
1042
item = await get_item_by_name(item_name)
1043
if not item:
1044
raise ItemNotFound("item", item_name)
1045
1046
# Check balance
1047
if user_data['balance'] < item['price']:
1048
raise InsufficientBalance(ctx.author.id, item['price'], user_data['balance'])
1049
1050
# Process purchase
1051
await process_purchase(ctx.author.id, item['id'])
1052
1053
embed = nextcord.Embed(
1054
title="🛒 Purchase Successful",
1055
description=f"You bought {item['name']} for {item['price']} coins!",
1056
color=nextcord.Color.green()
1057
)
1058
await ctx.send(embed=embed)
1059
1060
except BotException as e:
1061
# Handle custom bot errors
1062
await error_handler.handle_error(e, ctx=ctx)
1063
1064
except Exception as e:
1065
# Handle unexpected errors
1066
await error_handler.handle_error(e, ctx=ctx)
1067
1068
# Global error handlers using the error handler
1069
@bot.event
1070
async def on_command_error(ctx: commands.Context, error: commands.CommandError):
1071
"""Enhanced global command error handler."""
1072
await error_handler.handle_error(error, ctx=ctx)
1073
1074
@bot.event
1075
async def on_application_command_error(interaction: nextcord.Interaction, error: Exception):
1076
"""Enhanced application command error handler."""
1077
await error_handler.handle_error(error, interaction=interaction)
1078
```
1079
1080
This comprehensive documentation covers all aspects of nextcord's error handling system, providing developers with robust tools for managing exceptions and creating reliable Discord bot applications with proper error recovery mechanisms.