> ## Documentation Index
> Fetch the complete documentation index at: https://docs.confess.best/llms.txt
> Use this file to discover all available pages before exploring further.

# Handling Errors and Exceptions in alterself Selfbots

> Learn how to catch and handle alterself exceptions including HTTPError, SessionClosed, CaptchaChallenge, and command errors in your selfbot.

Every error that alterself raises derives from a single base class, `AlterselfError`, so you can always write a broad catch at the top of your stack and narrow it down as needed. Understanding the exception hierarchy lets you write targeted handlers that recover gracefully from transient failures, surface useful diagnostics for permanent ones, and avoid silently swallowing errors that require your attention.

***

## Exception Hierarchy

```text theme={null}
AlterselfError
├── HTTPError              - HTTP request failed (4xx, 5xx)
├── GatewayError           - WebSocket-level failure
│   └── SessionClosed      - Gateway connection closed
├── CommandError           - Command pipeline failure
│   ├── CheckFailed        - A check decorator returned falsy
│   ├── ConversionFailed   - Type converter raised an exception
│   ├── CommandNotFound    - No command matched the invocation
│   └── MissingArgument    - Required argument absent
└── CaptchaChallenge       - Discord requires a captcha
```

***

## HTTPError

`HTTPError` is raised whenever Discord's REST API responds with a 4xx or 5xx status code. The exception exposes the raw HTTP status, Discord's internal error code, and the human-readable message from the response body.

```python theme={null}
import alterself

bot = alterself.Client(token="TOKEN", prefix=".")

@bot.command()
async def safe_send(ctx, *, text: str):
    try:
        await bot.http.send_message(ctx.channel_id, content=text)
    except alterself.HTTPError as e:
        print(f"HTTP {e.status} - Discord code {e.code}: {e.text}")
        await ctx.reply(f"❌ Failed to send: `{e.text}`")

bot.run()
```

### Common Discord Error Codes

| Code | Meaning |
| - | - |
| `10003` | Unknown channel |
| `10004` | Unknown guild |
| `10008` | Unknown message |
| `50001` | Missing access |
| `50013` | Missing permissions |
| `50035` | Invalid form body |

<Tip>
  Check `e.code` (the Discord JSON error code) rather than `e.status` (the HTTP status) for precise branching. Many different error conditions all return `400 Bad Request` but carry distinct `code` values.
</Tip>

***

## SessionClosed

`SessionClosed` is raised when Discord closes the WebSocket connection with a non-resumable close code. alterself will **not** attempt to reconnect automatically when this exception is raised, because most fatal close codes indicate a configuration problem rather than a transient network hiccup.

```python theme={null}
try:
    bot.run()
except alterself.SessionClosed as e:
    print(f"Gateway closed - code {e.code}")
```

### Fatal Gateway Close Codes

| Code | Meaning |
| - | - |
| `4004` | Token invalid |
| `4010` | Invalid shard |
| `4011` | Sharding required |
| `4012` | Invalid API version |
| `4013` | Invalid intents |
| `4014` | Disallowed intents |

<Warning>
  Close code `4004` means your token is invalid or has been invalidated (e.g. password changed, token reset). This is unrecoverable, so no amount of reconnection will help. Check your token immediately and update it before restarting the bot.
</Warning>

***

## CaptchaChallenge

When Discord decides it needs human verification for an action (usually account-sensitive HTTP requests), it responds with a captcha challenge rather than the expected data. alterself surfaces this as a `CaptchaChallenge` exception instead of silently failing.

```python theme={null}
try:
    await bot.http.join_guild("some-invite")
except alterself.CaptchaChallenge as e:
    print(f"Captcha required - sitekey: {e.sitekey}")
    print(f"rqdata: {e.rqdata}")
    # Pass e.sitekey and e.rqdata to your captcha-solving service
```

The two key attributes are:

| Attribute | Description |
| - | - |
| `e.sitekey` | The hCaptcha site key needed to generate a solution token |
| `e.rqdata` | The `rqdata` value required by Discord's captcha implementation |

Pass both to your chosen captcha-solving service, retrieve the solution token, then retry the request with `captcha_key=<token>` in the request body.

***

## CommandError and its Subclasses

`CommandError` and its subclasses are raised inside the command dispatch pipeline. You handle them by registering an `on_command_error` event listener.

```python theme={null}
@bot.on_event
async def on_command_error(ctx, error):
    if isinstance(error, alterself.ConversionFailed):
        await ctx.reply(f"❌ Bad argument: `{error.argument}` couldn't be converted.")
    elif isinstance(error, alterself.MissingArgument):
        await ctx.reply(f"❌ Missing required argument: `{error.param}`.")
    elif isinstance(error, alterself.CommandNotFound):
        pass   # silently ignore unknown commands
    else:
        raise error   # re-raise anything unexpected
```

### CheckFailed

`CheckFailed` is raised when a `@bot.check` or `@command.check` decorator returns a falsy value. By default, alterself silently drops the invocation, so the command simply does not run and no error message is sent. If you want to notify the user, handle it explicitly in `on_command_error`:

```python theme={null}
@bot.on_event
async def on_command_error(ctx, error):
    if isinstance(error, alterself.CheckFailed):
        await ctx.reply("🚫 You don't have permission to use that command.")
```

<Note>
  `CheckFailed` is intentionally silent by default. This keeps your selfbot from drawing attention when commands are triggered by other users who don't meet your access requirements.
</Note>

***

## Global Error Handler

For a production selfbot, register a single `on_command_error` handler that covers every `CommandError` subclass and logs everything else:

```python theme={null}
import traceback

@bot.on_event
async def on_command_error(ctx, error):
    if isinstance(error, alterself.CheckFailed):
        return   # silently skip

    if isinstance(error, alterself.MissingArgument):
        await ctx.reply(f"❌ Missing argument: `{error.param}`.")
        return

    if isinstance(error, alterself.ConversionFailed):
        await ctx.reply(f"❌ Invalid value for `{error.param}`.")
        return

    if isinstance(error, alterself.CommandNotFound):
        return   # ignore unknown prefixed messages

    # Anything else is unexpected. Log the full traceback.
    traceback.print_exception(type(error), error, error.__traceback__)
```
