or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

application-commands.mdautomod.mdchannels-messaging.mdclient-bot.mdcommand-framework.mderror-handling.mdevents-gateway.mdguild-management.mdindex.mdinteractions-ui.mdlocalization-i18n.mdpermissions-security.mdpolls.mdusers-members.mdvoice-audio.md

error-handling.mddocs/

0

# Error Handling

1

2

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.

3

4

## Capabilities

5

6

### Base Discord Exceptions

7

8

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

9

10

```python { .api }

11

class DiscordException(Exception):

12

"""Base exception class for all Discord-related errors."""

13

14

class ClientException(DiscordException):

15

"""Exception for client operation failures."""

16

17

class InvalidData(ClientException):

18

"""Exception for malformed or invalid data from Discord API."""

19

20

class InvalidArgument(ClientException):

21

"""Exception for invalid arguments passed to functions."""

22

23

class LoginFailure(ClientException):

24

"""Exception for bot authentication failures."""

25

26

class NoMoreItems(ClientException):

27

"""Exception when async iterator has no more items."""

28

29

class GatewayNotFound(DiscordException):

30

"""Exception when gateway URL cannot be found."""

31

32

class ConnectionClosed(ClientException):

33

"""Exception when WebSocket connection is closed."""

34

35

def __init__(self, socket: WebSocketClientProtocol, *, shard_id: Optional[int] = None):

36

"""

37

Initialize connection closed exception.

38

39

Parameters:

40

- socket: WebSocket connection

41

- shard_id: Shard ID if applicable

42

"""

43

44

code: int

45

reason: str

46

shard_id: Optional[int]

47

48

class PrivilegedIntentsRequired(ClientException):

49

"""Exception for missing privileged intents."""

50

51

def __init__(self, shard_id: Optional[int] = None):

52

"""

53

Initialize privileged intents exception.

54

55

Parameters:

56

- shard_id: Shard ID if applicable

57

"""

58

59

shard_id: Optional[int]

60

61

class SessionStartLimitReached(ClientException):

62

"""Exception when session start limit is reached."""

63

```

64

65

### HTTP Exceptions

66

67

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

68

69

```python { .api }

70

class HTTPException(DiscordException):

71

"""Exception for HTTP request failures."""

72

73

def __init__(self, response: ClientResponse, message: Union[str, Dict[str, Any]]):

74

"""

75

Initialize HTTP exception.

76

77

Parameters:

78

- response: HTTP response object

79

- message: Error message or error data

80

"""

81

82

response: ClientResponse

83

status: int

84

code: int

85

text: str

86

87

class Forbidden(HTTPException):

88

"""Exception for 403 Forbidden HTTP errors."""

89

90

class NotFound(HTTPException):

91

"""Exception for 404 Not Found HTTP errors."""

92

93

class DiscordServerError(HTTPException):

94

"""Exception for 5xx server errors from Discord."""

95

96

class RateLimited(HTTPException):

97

"""Exception for rate limit errors (429)."""

98

99

def __init__(self, response: ClientResponse, message: Dict[str, Any]):

100

"""

101

Initialize rate limit exception.

102

103

Parameters:

104

- response: HTTP response

105

- message: Rate limit data

106

"""

107

108

retry_after: float

109

is_global: bool

110

```

111

112

### Interaction Exceptions

113

114

Errors specific to Discord interactions and UI components.

115

116

```python { .api }

117

class InteractionException(DiscordException):

118

"""Base exception for interaction-related errors."""

119

120

class InteractionResponded(InteractionException):

121

"""Exception when interaction has already been responded to."""

122

123

def __init__(self, interaction: Interaction):

124

"""

125

Initialize interaction responded exception.

126

127

Parameters:

128

- interaction: The interaction that was already responded to

129

"""

130

131

interaction: Interaction

132

133

class InteractionNotResponded(InteractionException):

134

"""Exception when interaction has not been responded to."""

135

136

def __init__(self, interaction: Interaction):

137

"""

138

Initialize interaction not responded exception.

139

140

Parameters:

141

- interaction: The interaction that needs a response

142

"""

143

144

interaction: Interaction

145

146

class InteractionTimedOut(InteractionException):

147

"""Exception when interaction times out."""

148

149

def __init__(self, interaction: Interaction):

150

"""

151

Initialize interaction timeout exception.

152

153

Parameters:

154

- interaction: The timed out interaction

155

"""

156

157

interaction: Interaction

158

159

class InteractionNotEditable(InteractionException):

160

"""Exception when interaction response cannot be edited."""

161

162

def __init__(self, interaction: Interaction):

163

"""

164

Initialize interaction not editable exception.

165

166

Parameters:

167

- interaction: The non-editable interaction

168

"""

169

170

interaction: Interaction

171

172

class ModalChainNotSupported(InteractionException):

173

"""Exception when modal chaining is attempted but not supported."""

174

175

class WebhookTokenMissing(DiscordException):

176

"""Exception when webhook token is required but missing."""

177

178

class LocalizationKeyError(DiscordException):

179

"""Exception for localization key errors."""

180

181

def __init__(self, key: str):

182

"""

183

Initialize localization key error.

184

185

Parameters:

186

- key: The missing localization key

187

"""

188

189

key: str

190

```

191

192

### Command Framework Exceptions

193

194

Comprehensive error handling for the message-based command framework.

195

196

```python { .api }

197

class CommandError(DiscordException):

198

"""Base exception for command-related errors."""

199

200

def __init__(self, message: str = None, *args):

201

"""

202

Initialize command error.

203

204

Parameters:

205

- message: Error message

206

- args: Additional arguments

207

"""

208

209

class CommandNotFound(CommandError):

210

"""Exception when command is not found."""

211

212

class MissingRequiredArgument(UserInputError):

213

"""Exception for missing required command arguments."""

214

215

def __init__(self, param: Parameter):

216

"""

217

Initialize missing argument exception.

218

219

Parameters:

220

- param: Missing parameter information

221

"""

222

223

param: Parameter

224

225

class TooManyArguments(UserInputError):

226

"""Exception when too many arguments are provided."""

227

228

class BadArgument(UserInputError):

229

"""Exception for invalid argument values or types."""

230

231

class BadUnionArgument(UserInputError):

232

"""Exception for failed union type conversions."""

233

234

def __init__(self, param: Parameter, converters: List[Converter], errors: List[CommandError]):

235

"""

236

Initialize bad union argument exception.

237

238

Parameters:

239

- param: Parameter that failed conversion

240

- converters: Attempted converters

241

- errors: Conversion errors

242

"""

243

244

param: Parameter

245

converters: List[Converter]

246

errors: List[CommandError]

247

248

class BadLiteralArgument(UserInputError):

249

"""Exception for failed literal type conversions."""

250

251

def __init__(self, param: Parameter, literals: List[Any], errors: List[CommandError]):

252

"""

253

Initialize bad literal argument exception.

254

255

Parameters:

256

- param: Parameter that failed conversion

257

- literals: Expected literal values

258

- errors: Conversion errors

259

"""

260

261

param: Parameter

262

literals: List[Any]

263

errors: List[CommandError]

264

265

class ArgumentParsingError(UserInputError):

266

"""Exception for general argument parsing failures."""

267

268

class UnexpectedQuoteError(ArgumentParsingError):

269

"""Exception for unexpected quotes in arguments."""

270

271

def __init__(self, quote: str):

272

"""

273

Initialize unexpected quote error.

274

275

Parameters:

276

- quote: The unexpected quote character

277

"""

278

279

quote: str

280

281

class InvalidEndOfQuotedStringError(ArgumentParsingError):

282

"""Exception for invalid end of quoted string."""

283

284

def __init__(self, char: str):

285

"""

286

Initialize invalid end of quoted string error.

287

288

Parameters:

289

- char: The invalid character

290

"""

291

292

char: str

293

294

class ExpectedClosingQuoteError(ArgumentParsingError):

295

"""Exception for missing closing quote."""

296

297

def __init__(self, close_quote: str):

298

"""

299

Initialize expected closing quote error.

300

301

Parameters:

302

- close_quote: Expected closing quote character

303

"""

304

305

close_quote: str

306

307

class CheckFailure(CommandError):

308

"""Exception when command check fails."""

309

310

class CheckAnyFailure(CheckFailure):

311

"""Exception when all command checks fail."""

312

313

def __init__(self, checks: List[Check], errors: List[CheckFailure]):

314

"""

315

Initialize check any failure exception.

316

317

Parameters:

318

- checks: Failed checks

319

- errors: Individual check errors

320

"""

321

322

checks: List[Check]

323

errors: List[CheckFailure]

324

325

class PrivateMessageOnly(CheckFailure):

326

"""Exception for DM-only commands used in guilds."""

327

328

class NoPrivateMessage(CheckFailure):

329

"""Exception for guild-only commands used in DMs."""

330

331

class NotOwner(CheckFailure):

332

"""Exception when non-owner tries to use owner-only command."""

333

334

class MissingRole(CheckFailure):

335

"""Exception for missing required role."""

336

337

def __init__(self, missing_role: Union[str, int]):

338

"""

339

Initialize missing role exception.

340

341

Parameters:

342

- missing_role: Required role name or ID

343

"""

344

345

missing_role: Union[str, int]

346

347

class BotMissingRole(CheckFailure):

348

"""Exception when bot is missing required role."""

349

350

def __init__(self, missing_role: Union[str, int]):

351

"""

352

Initialize bot missing role exception.

353

354

Parameters:

355

- missing_role: Required role name or ID

356

"""

357

358

missing_role: Union[str, int]

359

360

class MissingAnyRole(CheckFailure):

361

"""Exception when missing any of the required roles."""

362

363

def __init__(self, missing_roles: List[Union[str, int]]):

364

"""

365

Initialize missing any role exception.

366

367

Parameters:

368

- missing_roles: List of required role names or IDs

369

"""

370

371

missing_roles: List[Union[str, int]]

372

373

class BotMissingAnyRole(CheckFailure):

374

"""Exception when bot is missing any of the required roles."""

375

376

def __init__(self, missing_roles: List[Union[str, int]]):

377

"""

378

Initialize bot missing any role exception.

379

380

Parameters:

381

- missing_roles: List of required role names or IDs

382

"""

383

384

missing_roles: List[Union[str, int]]

385

386

class MissingPermissions(CheckFailure):

387

"""Exception for missing required permissions."""

388

389

def __init__(self, missing_permissions: List[str]):

390

"""

391

Initialize missing permissions exception.

392

393

Parameters:

394

- missing_permissions: List of missing permission names

395

"""

396

397

missing_permissions: List[str]

398

399

class BotMissingPermissions(CheckFailure):

400

"""Exception when bot is missing required permissions."""

401

402

def __init__(self, missing_permissions: List[str]):

403

"""

404

Initialize bot missing permissions exception.

405

406

Parameters:

407

- missing_permissions: List of missing permission names

408

"""

409

410

missing_permissions: List[str]

411

412

class NSFWChannelRequired(CheckFailure):

413

"""Exception for NSFW commands used in non-NSFW channels."""

414

415

class DisabledCommand(CommandError):

416

"""Exception when command is disabled."""

417

418

class CommandInvokeError(CommandError):

419

"""Exception wrapping errors during command execution."""

420

421

def __init__(self, e: Exception):

422

"""

423

Initialize command invoke error.

424

425

Parameters:

426

- e: Original exception that was raised

427

"""

428

429

original: Exception

430

431

class CommandOnCooldown(CommandError):

432

"""Exception when command is on cooldown."""

433

434

def __init__(self, cooldown: Cooldown, retry_after: float, type: BucketType):

435

"""

436

Initialize command on cooldown exception.

437

438

Parameters:

439

- cooldown: Cooldown configuration

440

- retry_after: Time until command can be used again

441

- type: Cooldown bucket type

442

"""

443

444

cooldown: Cooldown

445

retry_after: float

446

type: BucketType

447

448

class MaxConcurrencyReached(CommandError):

449

"""Exception when maximum command concurrency is reached."""

450

451

def __init__(self, number: int, per: BucketType):

452

"""

453

Initialize max concurrency exception.

454

455

Parameters:

456

- number: Maximum allowed concurrent uses

457

- per: Concurrency bucket type

458

"""

459

460

number: int

461

per: BucketType

462

463

class UserInputError(CommandError):

464

"""Base exception for user input errors."""

465

466

class ConversionError(CommandError):

467

"""Exception for type conversion failures."""

468

469

def __init__(self, converter: Converter, original: Exception):

470

"""

471

Initialize conversion error.

472

473

Parameters:

474

- converter: Converter that failed

475

- original: Original exception

476

"""

477

478

converter: Converter

479

original: Exception

480

481

class ExtensionError(DiscordException):

482

"""Base exception for extension-related errors."""

483

484

def __init__(self, message: str = None, *args, name: str):

485

"""

486

Initialize extension error.

487

488

Parameters:

489

- message: Error message

490

- args: Additional arguments

491

- name: Extension name

492

"""

493

494

name: str

495

496

class ExtensionAlreadyLoaded(ExtensionError):

497

"""Exception when extension is already loaded."""

498

499

class ExtensionNotLoaded(ExtensionError):

500

"""Exception when extension is not loaded."""

501

502

class NoEntryPointError(ExtensionError):

503

"""Exception when extension has no setup function."""

504

505

class ExtensionFailed(ExtensionError):

506

"""Exception when extension fails to load."""

507

508

def __init__(self, name: str, original: Exception):

509

"""

510

Initialize extension failed exception.

511

512

Parameters:

513

- name: Extension name

514

- original: Original exception

515

"""

516

517

original: Exception

518

519

class ExtensionNotFound(ExtensionError):

520

"""Exception when extension module is not found."""

521

```

522

523

### Application Command Exceptions

524

525

Errors specific to slash commands and application commands.

526

527

```python { .api }

528

class ApplicationCommandError(DiscordException):

529

"""Base exception for application command errors."""

530

531

class ApplicationCommandInvokeError(ApplicationCommandError):

532

"""Exception wrapping errors during application command execution."""

533

534

def __init__(self, e: Exception):

535

"""

536

Initialize application command invoke error.

537

538

Parameters:

539

- e: Original exception that was raised

540

"""

541

542

original: Exception

543

544

class ApplicationCommandOnCooldown(ApplicationCommandError):

545

"""Exception when application command is on cooldown."""

546

547

def __init__(self, cooldown: Cooldown, retry_after: float, type: BucketType):

548

"""

549

Initialize application command cooldown exception.

550

551

Parameters:

552

- cooldown: Cooldown configuration

553

- retry_after: Time until command can be used again

554

- type: Cooldown bucket type

555

"""

556

557

cooldown: Cooldown

558

retry_after: float

559

type: BucketType

560

561

class ApplicationCommandCheckFailure(ApplicationCommandError):

562

"""Exception when application command check fails."""

563

```

564

565

### Error Event Handlers

566

567

Event system for handling and responding to various error conditions.

568

569

```python { .api }

570

@bot.event

571

async def on_error(event: str, *args, **kwargs):

572

"""

573

Called when an error occurs in event handlers.

574

575

Parameters:

576

- event: Event name that caused the error

577

- args: Event arguments

578

- kwargs: Event keyword arguments

579

"""

580

581

@bot.event

582

async def on_command_error(ctx: Context, error: CommandError):

583

"""

584

Called when command execution raises an error.

585

586

Parameters:

587

- ctx: Command context

588

- error: Exception that was raised

589

"""

590

591

@bot.event

592

async def on_application_command_error(inter: ApplicationCommandInteraction, error: Exception):

593

"""

594

Called when application command execution raises an error.

595

596

Parameters:

597

- inter: Application command interaction

598

- error: Exception that was raised

599

"""

600

601

async def on_slash_command_error(inter: ApplicationCommandInteraction, error: Exception):

602

"""

603

Called when slash command execution raises an error.

604

605

Parameters:

606

- inter: Slash command interaction

607

- error: Exception that was raised

608

"""

609

610

async def on_user_command_error(inter: ApplicationCommandInteraction, error: Exception):

611

"""

612

Called when user command execution raises an error.

613

614

Parameters:

615

- inter: User command interaction

616

- error: Exception that was raised

617

"""

618

619

async def on_message_command_error(inter: ApplicationCommandInteraction, error: Exception):

620

"""

621

Called when message command execution raises an error.

622

623

Parameters:

624

- inter: Message command interaction

625

- error: Exception that was raised

626

"""

627

```

628

629

### Error Recovery and Logging

630

631

Utilities for error recovery, logging, and debugging assistance.

632

633

```python { .api }

634

class ErrorHandler:

635

"""Error handling and recovery utilities."""

636

637

def __init__(self, bot: Bot):

638

self.bot = bot

639

self.error_log = []

640

self.error_counts = defaultdict(int)

641

642

def log_error(self, error: Exception, context: str = None):

643

"""

644

Log error with context information.

645

646

Parameters:

647

- error: Exception to log

648

- context: Additional context information

649

"""

650

651

def get_error_embed(self, error: Exception, ctx: Context = None) -> Embed:

652

"""

653

Create error embed for user display.

654

655

Parameters:

656

- error: Exception to format

657

- ctx: Command context if available

658

659

Returns:

660

Formatted error embed

661

"""

662

663

async def handle_http_error(self, error: HTTPException, ctx: Context = None) -> bool:

664

"""

665

Handle HTTP errors with appropriate user feedback.

666

667

Parameters:

668

- error: HTTP exception

669

- ctx: Command context if available

670

671

Returns:

672

True if error was handled

673

"""

674

675

async def handle_permission_error(self, error: CheckFailure, ctx: Context) -> bool:

676

"""

677

Handle permission-related errors.

678

679

Parameters:

680

- error: Permission check failure

681

- ctx: Command context

682

683

Returns:

684

True if error was handled

685

"""

686

687

async def handle_cooldown_error(self, error: CommandOnCooldown, ctx: Context) -> bool:

688

"""

689

Handle cooldown errors with retry information.

690

691

Parameters:

692

- error: Cooldown exception

693

- ctx: Command context

694

695

Returns:

696

True if error was handled

697

"""

698

699

async def handle_argument_error(self, error: UserInputError, ctx: Context) -> bool:

700

"""

701

Handle argument parsing and conversion errors.

702

703

Parameters:

704

- error: User input error

705

- ctx: Command context

706

707

Returns:

708

True if error was handled

709

"""

710

711

def format_traceback(error: Exception) -> str:

712

"""

713

Format exception traceback for logging.

714

715

Parameters:

716

- error: Exception to format

717

718

Returns:

719

Formatted traceback string

720

"""

721

722

def get_error_cause(error: Exception) -> str:

723

"""

724

Get human-readable error cause.

725

726

Parameters:

727

- error: Exception to analyze

728

729

Returns:

730

Error cause description

731

"""

732

733

async def safe_send(channel: Messageable, content: str = None, **kwargs) -> Optional[Message]:

734

"""

735

Safely send message with error handling.

736

737

Parameters:

738

- channel: Channel to send to

739

- content: Message content

740

- kwargs: Additional send parameters

741

742

Returns:

743

Sent message if successful, None otherwise

744

"""

745

746

def retry_on_error(*exceptions: Type[Exception], max_retries: int = 3, delay: float = 1.0):

747

"""

748

Decorator for retrying operations on specific errors.

749

750

Parameters:

751

- exceptions: Exception types to retry on

752

- max_retries: Maximum number of retry attempts

753

- delay: Delay between retries in seconds

754

755

Returns:

756

Decorated function with retry logic

757

"""

758

759

async def with_timeout(coro: Awaitable, timeout: float, default: Any = None) -> Any:

760

"""

761

Execute coroutine with timeout protection.

762

763

Parameters:

764

- coro: Coroutine to execute

765

- timeout: Timeout in seconds

766

- default: Default value on timeout

767

768

Returns:

769

Coroutine result or default value

770

"""

771

```

772

773

## Usage Examples

774

775

### Basic Error Handling Setup

776

777

```python

778

import disnake

779

from disnake.ext import commands

780

import traceback

781

import logging

782

783

# Set up logging

784

logging.basicConfig(level=logging.INFO)

785

logger = logging.getLogger('discord_bot')

786

787

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

788

789

@bot.event

790

async def on_ready():

791

print(f'Bot ready: {bot.user}')

792

793

@bot.event

794

async def on_error(event, *args, **kwargs):

795

"""Handle errors in event handlers."""

796

logger.error(f'Error in event {event}:', exc_info=True)

797

798

# Log to error channel if available

799

error_channel = bot.get_channel(ERROR_CHANNEL_ID) # Replace with actual channel ID

800

if error_channel:

801

embed = disnake.Embed(

802

title=f"Error in {event}",

803

description=f"```py\n{traceback.format_exc()[:1900]}\n```",

804

color=0xff0000,

805

timestamp=disnake.utils.utcnow()

806

)

807

try:

808

await error_channel.send(embed=embed)

809

except:

810

pass # Avoid error loops

811

812

@bot.event

813

async def on_command_error(ctx, error):

814

"""Comprehensive command error handling."""

815

816

# Ignore these errors

817

if isinstance(error, (commands.CommandNotFound, commands.DisabledCommand)):

818

return

819

820

# Extract original error from CommandInvokeError

821

if isinstance(error, commands.CommandInvokeError):

822

error = error.original

823

824

# User input errors

825

elif isinstance(error, commands.MissingRequiredArgument):

826

embed = disnake.Embed(

827

title="Missing Argument",

828

description=f"You're missing the `{error.param.name}` parameter.",

829

color=0xff9900

830

)

831

embed.add_field(name="Usage", value=f"`{ctx.prefix}{ctx.command.qualified_name} {ctx.command.signature}`")

832

await ctx.send(embed=embed)

833

return

834

835

elif isinstance(error, commands.BadArgument):

836

embed = disnake.Embed(

837

title="Invalid Argument",

838

description=f"Invalid argument provided: {error}",

839

color=0xff9900

840

)

841

embed.add_field(name="Usage", value=f"`{ctx.prefix}{ctx.command.qualified_name} {ctx.command.signature}`")

842

await ctx.send(embed=embed)

843

return

844

845

elif isinstance(error, commands.TooManyArguments):

846

embed = disnake.Embed(

847

title="Too Many Arguments",

848

description="You provided too many arguments for this command.",

849

color=0xff9900

850

)

851

embed.add_field(name="Usage", value=f"`{ctx.prefix}{ctx.command.qualified_name} {ctx.command.signature}`")

852

await ctx.send(embed=embed)

853

return

854

855

# Permission errors

856

elif isinstance(error, commands.MissingPermissions):

857

missing = ', '.join(error.missing_permissions)

858

embed = disnake.Embed(

859

title="Missing Permissions",

860

description=f"You need the following permissions: **{missing}**",

861

color=0xff0000

862

)

863

await ctx.send(embed=embed)

864

return

865

866

elif isinstance(error, commands.BotMissingPermissions):

867

missing = ', '.join(error.missing_permissions)

868

embed = disnake.Embed(

869

title="Bot Missing Permissions",

870

description=f"I need the following permissions: **{missing}**",

871

color=0xff0000

872

)

873

await ctx.send(embed=embed)

874

return

875

876

elif isinstance(error, commands.MissingRole):

877

embed = disnake.Embed(

878

title="Missing Role",

879

description=f"You need the **{error.missing_role}** role to use this command.",

880

color=0xff0000

881

)

882

await ctx.send(embed=embed)

883

return

884

885

elif isinstance(error, commands.MissingAnyRole):

886

roles = ', '.join(str(role) for role in error.missing_roles)

887

embed = disnake.Embed(

888

title="Missing Role",

889

description=f"You need one of these roles: **{roles}**",

890

color=0xff0000

891

)

892

await ctx.send(embed=embed)

893

return

894

895

elif isinstance(error, commands.NotOwner):

896

embed = disnake.Embed(

897

title="Owner Only",

898

description="Only the bot owner can use this command.",

899

color=0xff0000

900

)

901

await ctx.send(embed=embed)

902

return

903

904

elif isinstance(error, commands.NoPrivateMessage):

905

embed = disnake.Embed(

906

title="Guild Only",

907

description="This command cannot be used in private messages.",

908

color=0xff0000

909

)

910

await ctx.send(embed=embed)

911

return

912

913

elif isinstance(error, commands.PrivateMessageOnly):

914

embed = disnake.Embed(

915

title="DM Only",

916

description="This command can only be used in private messages.",

917

color=0xff0000

918

)

919

await ctx.send(embed=embed)

920

return

921

922

elif isinstance(error, commands.NSFWChannelRequired):

923

embed = disnake.Embed(

924

title="NSFW Channel Required",

925

description="This command can only be used in NSFW channels.",

926

color=0xff0000

927

)

928

await ctx.send(embed=embed)

929

return

930

931

# Cooldown errors

932

elif isinstance(error, commands.CommandOnCooldown):

933

embed = disnake.Embed(

934

title="Command on Cooldown",

935

description=f"Try again in **{error.retry_after:.2f}** seconds.",

936

color=0xff9900

937

)

938

await ctx.send(embed=embed, delete_after=error.retry_after)

939

return

940

941

elif isinstance(error, commands.MaxConcurrencyReached):

942

embed = disnake.Embed(

943

title="Max Concurrency Reached",

944

description="This command is already being used by too many people. Try again later.",

945

color=0xff9900

946

)

947

await ctx.send(embed=embed)

948

return

949

950

# HTTP errors

951

elif isinstance(error, disnake.Forbidden):

952

embed = disnake.Embed(

953

title="Forbidden",

954

description="I don't have permission to perform this action.",

955

color=0xff0000

956

)

957

await ctx.send(embed=embed)

958

return

959

960

elif isinstance(error, disnake.NotFound):

961

embed = disnake.Embed(

962

title="Not Found",

963

description="The requested resource could not be found.",

964

color=0xff0000

965

)

966

await ctx.send(embed=embed)

967

return

968

969

elif isinstance(error, disnake.HTTPException):

970

embed = disnake.Embed(

971

title="HTTP Error",

972

description=f"An HTTP error occurred: {error.text}",

973

color=0xff0000

974

)

975

await ctx.send(embed=embed)

976

return

977

978

# Unknown error

979

else:

980

embed = disnake.Embed(

981

title="Unexpected Error",

982

description="An unexpected error occurred. The developers have been notified.",

983

color=0xff0000

984

)

985

await ctx.send(embed=embed)

986

987

# Log the error

988

logger.error(f'Unexpected error in command {ctx.command}:', exc_info=error)

989

990

# Send to error channel

991

error_channel = bot.get_channel(ERROR_CHANNEL_ID)

992

if error_channel:

993

error_embed = disnake.Embed(

994

title=f"Command Error: {ctx.command.qualified_name}",

995

color=0xff0000,

996

timestamp=disnake.utils.utcnow()

997

)

998

error_embed.add_field(name="User", value=f"{ctx.author} ({ctx.author.id})", inline=True)

999

error_embed.add_field(name="Guild", value=f"{ctx.guild} ({ctx.guild.id})" if ctx.guild else "DM", inline=True)

1000

error_embed.add_field(name="Channel", value=f"#{ctx.channel} ({ctx.channel.id})", inline=True)

1001

error_embed.add_field(name="Command", value=f"`{ctx.message.content}`", inline=False)

1002

error_embed.add_field(

1003

name="Error",

1004

value=f"```py\n{traceback.format_exception(type(error), error, error.__traceback__)[-1][:1000]}\n```",

1005

inline=False

1006

)

1007

1008

try:

1009

await error_channel.send(embed=error_embed)

1010

except:

1011

pass

1012

1013

bot.run('YOUR_BOT_TOKEN')

1014

```

1015

1016

### Application Command Error Handling

1017

1018

```python

1019

@bot.event

1020

async def on_application_command_error(inter, error):

1021

"""Handle application command errors."""

1022

1023

# Extract original error

1024

if isinstance(error, disnake.ApplicationCommandInvokeError):

1025

error = error.original

1026

1027

# Create base embed

1028

embed = disnake.Embed(color=0xff0000, timestamp=disnake.utils.utcnow())

1029

1030

# Handle specific errors

1031

if isinstance(error, commands.MissingPermissions):

1032

missing = ', '.join(error.missing_permissions)

1033

embed.title = "Missing Permissions"

1034

embed.description = f"You need: **{missing}**"

1035

1036

elif isinstance(error, commands.BotMissingPermissions):

1037

missing = ', '.join(error.missing_permissions)

1038

embed.title = "Bot Missing Permissions"

1039

embed.description = f"I need: **{missing}**"

1040

1041

elif isinstance(error, commands.MissingRole):

1042

embed.title = "Missing Role"

1043

embed.description = f"You need the **{error.missing_role}** role."

1044

1045

elif isinstance(error, commands.MissingAnyRole):

1046

roles = ', '.join(str(role) for role in error.missing_roles)

1047

embed.title = "Missing Role"

1048

embed.description = f"You need one of: **{roles}**"

1049

1050

elif isinstance(error, commands.NotOwner):

1051

embed.title = "Owner Only"

1052

embed.description = "Only the bot owner can use this command."

1053

1054

elif isinstance(error, commands.NoPrivateMessage):

1055

embed.title = "Guild Only"

1056

embed.description = "This command cannot be used in DMs."

1057

1058

elif isinstance(error, commands.CommandOnCooldown):

1059

embed.title = "Command on Cooldown"

1060

embed.description = f"Try again in **{error.retry_after:.2f}** seconds."

1061

embed.color = 0xff9900

1062

1063

elif isinstance(error, disnake.Forbidden):

1064

embed.title = "Forbidden"

1065

embed.description = "I don't have permission to perform this action."

1066

1067

elif isinstance(error, disnake.NotFound):

1068

embed.title = "Not Found"

1069

embed.description = "The requested resource was not found."

1070

1071

elif isinstance(error, ValueError):

1072

embed.title = "Invalid Input"

1073

embed.description = "Please check your input and try again."

1074

embed.color = 0xff9900

1075

1076

else:

1077

embed.title = "Unexpected Error"

1078

embed.description = "An unexpected error occurred."

1079

1080

# Log unexpected errors

1081

logger.error(f'Unexpected slash command error:', exc_info=error)

1082

1083

# Send error response

1084

try:

1085

if inter.response.is_done():

1086

await inter.followup.send(embed=embed, ephemeral=True)

1087

else:

1088

await inter.response.send_message(embed=embed, ephemeral=True)

1089

except:

1090

# If we can't send the error message, log it

1091

logger.error(f'Failed to send error response for interaction {inter.id}')

1092

1093

# Command-specific error handlers

1094

@bot.slash_command()

1095

async def divide(inter, a: float, b: float):

1096

"""Divide two numbers."""

1097

try:

1098

result = a / b

1099

await inter.response.send_message(f"{a} ÷ {b} = {result}")

1100

except ZeroDivisionError:

1101

embed = disnake.Embed(

1102

title="Math Error",

1103

description="Cannot divide by zero!",

1104

color=0xff0000

1105

)

1106

await inter.response.send_message(embed=embed, ephemeral=True)

1107

1108

@bot.slash_command()

1109

async def risky_operation(inter):

1110

"""Command that might fail."""

1111

try:

1112

# Potentially risky operation

1113

result = await some_api_call()

1114

await inter.response.send_message(f"Result: {result}")

1115

1116

except aiohttp.ClientError as e:

1117

embed = disnake.Embed(

1118

title="Network Error",

1119

description="Failed to connect to external service. Try again later.",

1120

color=0xff9900

1121

)

1122

await inter.response.send_message(embed=embed, ephemeral=True)

1123

logger.warning(f'API call failed: {e}')

1124

1125

except asyncio.TimeoutError:

1126

embed = disnake.Embed(

1127

title="Timeout",

1128

description="The operation timed out. Try again later.",

1129

color=0xff9900

1130

)

1131

await inter.response.send_message(embed=embed, ephemeral=True)

1132

1133

except Exception as e:

1134

embed = disnake.Embed(

1135

title="Error",

1136

description="An unexpected error occurred.",

1137

color=0xff0000

1138

)

1139

await inter.response.send_message(embed=embed, ephemeral=True)

1140

logger.error(f'Unexpected error in risky_operation: {e}', exc_info=True)

1141

```

1142

1143

### Advanced Error Recovery System

1144

1145

```python

1146

import asyncio

1147

import functools

1148

from typing import Optional, Callable, Any

1149

from datetime import datetime, timedelta

1150

1151

class ErrorHandler:

1152

"""Advanced error handling and recovery system."""

1153

1154

def __init__(self, bot):

1155

self.bot = bot

1156

self.error_counts = {}

1157

self.last_errors = {}

1158

self.recovery_strategies = {}

1159

1160

def register_recovery_strategy(self, error_type: type, strategy: Callable):

1161

"""Register a recovery strategy for specific error types."""

1162

self.recovery_strategies[error_type] = strategy

1163

1164

async def handle_error(self, error: Exception, context: dict = None) -> bool:

1165

"""

1166

Handle error with recovery strategies.

1167

1168

Returns True if error was handled and recovered from.

1169

"""

1170

error_type = type(error)

1171

error_key = f"{error_type.__name__}:{str(error)}"

1172

1173

# Track error frequency

1174

now = datetime.utcnow()

1175

if error_key not in self.error_counts:

1176

self.error_counts[error_key] = []

1177

1178

self.error_counts[error_key].append(now)

1179

1180

# Clean old entries (last hour)

1181

cutoff = now - timedelta(hours=1)

1182

self.error_counts[error_key] = [

1183

t for t in self.error_counts[error_key] if t > cutoff

1184

]

1185

1186

# Check if error is recurring

1187

recent_count = len(self.error_counts[error_key])

1188

if recent_count > 5:

1189

logger.warning(f'Recurring error detected: {error_key} ({recent_count} times)')

1190

1191

# Try recovery strategy

1192

if error_type in self.recovery_strategies:

1193

try:

1194

return await self.recovery_strategies[error_type](error, context)

1195

except Exception as recovery_error:

1196

logger.error(f'Recovery strategy failed: {recovery_error}')

1197

1198

return False

1199

1200

# Initialize error handler

1201

error_handler = ErrorHandler(bot)

1202

1203

# Recovery strategies

1204

async def recover_from_forbidden(error: disnake.Forbidden, context: dict) -> bool:

1205

"""Recovery strategy for Forbidden errors."""

1206

if context and 'channel' in context:

1207

channel = context['channel']

1208

1209

# Try to send error message to a different channel

1210

if hasattr(channel, 'guild') and channel.guild:

1211

# Find a channel we can send to

1212

for text_channel in channel.guild.text_channels:

1213

try:

1214

perms = text_channel.permissions_for(channel.guild.me)

1215

if perms.send_messages:

1216

await text_channel.send(

1217

f"⚠️ I don't have permission to perform an action in {channel.mention}. "

1218

f"Please check my permissions."

1219

)

1220

return True

1221

except:

1222

continue

1223

1224

return False

1225

1226

async def recover_from_not_found(error: disnake.NotFound, context: dict) -> bool:

1227

"""Recovery strategy for NotFound errors."""

1228

if context and 'interaction' in context:

1229

inter = context['interaction']

1230

1231

# If interaction is not found, it might have expired

1232

try:

1233

if not inter.response.is_done():

1234

await inter.response.send_message(

1235

"This interaction has expired. Please try the command again.",

1236

ephemeral=True

1237

)

1238

return True

1239

except:

1240

pass

1241

1242

return False

1243

1244

async def recover_from_rate_limit(error: disnake.HTTPException, context: dict) -> bool:

1245

"""Recovery strategy for rate limits."""

1246

if error.status == 429: # Rate limited

1247

retry_after = getattr(error, 'retry_after', 1)

1248

logger.info(f'Rate limited, waiting {retry_after} seconds')

1249

1250

await asyncio.sleep(retry_after)

1251

1252

# Try to notify about the delay

1253

if context and 'ctx' in context:

1254

ctx = context['ctx']

1255

try:

1256

await ctx.send(

1257

f"⏳ Rate limited. Retrying in {retry_after} seconds...",

1258

delete_after=retry_after + 5

1259

)

1260

except:

1261

pass

1262

1263

return True

1264

1265

return False

1266

1267

# Register recovery strategies

1268

error_handler.register_recovery_strategy(disnake.Forbidden, recover_from_forbidden)

1269

error_handler.register_recovery_strategy(disnake.NotFound, recover_from_not_found)

1270

error_handler.register_recovery_strategy(disnake.HTTPException, recover_from_rate_limit)

1271

1272

def with_error_recovery(func):

1273

"""Decorator to add error recovery to functions."""

1274

@functools.wraps(func)

1275

async def wrapper(*args, **kwargs):

1276

try:

1277

return await func(*args, **kwargs)

1278

except Exception as e:

1279

context = {

1280

'function': func.__name__,

1281

'args': args,

1282

'kwargs': kwargs

1283

}

1284

1285

# Try to extract useful context

1286

for arg in args:

1287

if isinstance(arg, (commands.Context, disnake.ApplicationCommandInteraction)):

1288

context['ctx'] = arg

1289

if hasattr(arg, 'channel'):

1290

context['channel'] = arg.channel

1291

if hasattr(arg, 'guild'):

1292

context['guild'] = arg.guild

1293

break

1294

1295

# Attempt recovery

1296

recovered = await error_handler.handle_error(e, context)

1297

1298

if not recovered:

1299

# Re-raise if recovery failed

1300

raise

1301

1302

return wrapper

1303

1304

# Retry decorator

1305

def retry_on_error(*error_types, max_retries=3, delay=1.0, backoff=2.0):

1306

"""Decorator to retry function calls on specific errors."""

1307

def decorator(func):

1308

@functools.wraps(func)

1309

async def wrapper(*args, **kwargs):

1310

last_exception = None

1311

1312

for attempt in range(max_retries + 1):

1313

try:

1314

return await func(*args, **kwargs)

1315

except error_types as e:

1316

last_exception = e

1317

1318

if attempt == max_retries:

1319

break

1320

1321

wait_time = delay * (backoff ** attempt)

1322

logger.info(f'Retrying {func.__name__} in {wait_time}s (attempt {attempt + 1}/{max_retries})')

1323

await asyncio.sleep(wait_time)

1324

1325

# All retries failed

1326

raise last_exception

1327

1328

return wrapper

1329

return decorator

1330

1331

# Usage examples

1332

@bot.command()

1333

@with_error_recovery

1334

@retry_on_error(disnake.HTTPException, max_retries=3, delay=2.0)

1335

async def reliable_command(ctx):

1336

"""A command with error recovery and retry logic."""

1337

# This might fail with HTTPException

1338

await ctx.send("This command has error recovery!")

1339

1340

@bot.slash_command()

1341

@with_error_recovery

1342

async def fetch_data(inter, url: str):

1343

"""Fetch data from URL with error handling."""

1344

try:

1345

async with aiohttp.ClientSession() as session:

1346

async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as resp:

1347

if resp.status == 200:

1348

data = await resp.text()

1349

await inter.response.send_message(f"Data length: {len(data)} characters")

1350

else:

1351

await inter.response.send_message(f"HTTP {resp.status}: {resp.reason}")

1352

1353

except asyncio.TimeoutError:

1354

await inter.response.send_message("⏰ Request timed out", ephemeral=True)

1355

1356

except aiohttp.ClientError as e:

1357

await inter.response.send_message(f"🌐 Network error: {e}", ephemeral=True)

1358

1359

# Safe execution context manager

1360

class SafeExecution:

1361

"""Context manager for safe code execution with error handling."""

1362

1363

def __init__(self, ctx_or_inter, *,

1364

error_message="An error occurred",

1365

log_errors=True,

1366

reraise=False):

1367

self.ctx_or_inter = ctx_or_inter

1368

self.error_message = error_message

1369

self.log_errors = log_errors

1370

self.reraise = reraise

1371

1372

async def __aenter__(self):

1373

return self

1374

1375

async def __aexit__(self, exc_type, exc_val, exc_tb):

1376

if exc_type is not None:

1377

if self.log_errors:

1378

logger.error(f'Error in safe execution: {exc_val}', exc_info=exc_val)

1379

1380

# Send error message

1381

try:

1382

if isinstance(self.ctx_or_inter, commands.Context):

1383

await self.ctx_or_inter.send(self.error_message)

1384

else:

1385

if self.ctx_or_inter.response.is_done():

1386

await self.ctx_or_inter.followup.send(self.error_message, ephemeral=True)

1387

else:

1388

await self.ctx_or_inter.response.send_message(self.error_message, ephemeral=True)

1389

except:

1390

pass # Avoid error loops

1391

1392

# Suppress exception unless reraise is True

1393

return not self.reraise

1394

1395

# Usage of SafeExecution

1396

@bot.command()

1397

async def safe_command(ctx):

1398

"""Command using safe execution context manager."""

1399

async with SafeExecution(ctx, error_message="Failed to process your request"):

1400

# Potentially risky operation

1401

result = await some_risky_operation()

1402

await ctx.send(f"Result: {result}")

1403

1404

# Error monitoring and alerts

1405

class ErrorMonitor:

1406

"""Monitor errors and send alerts when thresholds are exceeded."""

1407

1408

def __init__(self, bot, alert_channel_id: int, threshold: int = 10):

1409

self.bot = bot

1410

self.alert_channel_id = alert_channel_id

1411

self.threshold = threshold

1412

self.error_window = timedelta(minutes=15)

1413

self.recent_errors = []

1414

1415

def record_error(self, error: Exception, context: str = None):

1416

"""Record an error occurrence."""

1417

now = datetime.utcnow()

1418

self.recent_errors.append({

1419

'timestamp': now,

1420

'error': str(error),

1421

'type': type(error).__name__,

1422

'context': context

1423

})

1424

1425

# Clean old errors

1426

cutoff = now - self.error_window

1427

self.recent_errors = [e for e in self.recent_errors if e['timestamp'] > cutoff]

1428

1429

# Check if we should send an alert

1430

if len(self.recent_errors) >= self.threshold:

1431

asyncio.create_task(self._send_alert())

1432

1433

async def _send_alert(self):

1434

"""Send error alert to monitoring channel."""

1435

channel = self.bot.get_channel(self.alert_channel_id)

1436

if not channel:

1437

return

1438

1439

error_types = {}

1440

for error in self.recent_errors:

1441

error_type = error['type']

1442

error_types[error_type] = error_types.get(error_type, 0) + 1

1443

1444

embed = disnake.Embed(

1445

title="🚨 Error Alert",

1446

description=f"**{len(self.recent_errors)}** errors in the last 15 minutes",

1447

color=0xff0000,

1448

timestamp=disnake.utils.utcnow()

1449

)

1450

1451

error_summary = '\n'.join([f"**{error_type}**: {count}" for error_type, count in error_types.items()])

1452

embed.add_field(name="Error Types", value=error_summary, inline=False)

1453

1454

try:

1455

await channel.send(embed=embed)

1456

except:

1457

pass

1458

1459

# Clear recent errors after alert

1460

self.recent_errors.clear()

1461

1462

# Initialize error monitor

1463

error_monitor = ErrorMonitor(bot, ERROR_ALERT_CHANNEL_ID)

1464

1465

# Update error handlers to use monitor

1466

@bot.event

1467

async def on_command_error(ctx, error):

1468

"""Enhanced command error handler with monitoring."""

1469

error_monitor.record_error(error, f'Command: {ctx.command}')

1470

1471

# Original error handling code here...

1472

# [Previous error handling implementation]

1473

1474

@bot.event

1475

async def on_application_command_error(inter, error):

1476

"""Enhanced application command error handler with monitoring."""

1477

error_monitor.record_error(error, f'Slash Command: {inter.data.name}')

1478

1479

# Original error handling code here...

1480

# [Previous error handling implementation]

1481

```