or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

application-commands.mdchannels.mdclient.mdcommands.mderrors.mdevents.mdguild.mdindex.mdmessages.mdpermissions.mdtasks.mdui.mdusers.mdutilities.mdvoice.mdwebhooks.md

errors.mddocs/

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.