# The framework's bus keys

Subscribe to the events seedcord itself publishes on the bus, and see which transport publishes each one.

seedcord publishes its own keys on `core.bus`, alongside the keys you declare yourself. Without them, logging every deploy, tracing each interaction, or posting faults to a channel means wrapping your own handlers and deploy script. You subscribe to them with [`@Subscribe`](https://seedcord.org/docs/packages/core/latest/functions/subscribe) or `core.bus.on`, the same way you subscribe to [your own keys](https://seedcord.org/guide/events/bus/). You can't publish them yourself.

The `where` column says which transport publishes each key.

{/* prettier-ignore-start */}

| key                         | where   | fires when                                                                                               |
| --------------------------- | ------- | -------------------------------------------------------------------------------------------------------- |
| `unknownException`          | both    | a raw throw reaches the fault boundary, or the Node process catches an unhandled error                   |
| `handledException`          | both    | a [`Notice`](https://seedcord.org/docs/packages/core/latest/classes/notice) with `report` true is caught |
| `unhandledInteractionError` | both    | an interaction dispatch throws past the boundary                                                         |
| `interactionDispatched`     | both    | one dispatch finishes                                                                                    |
| `responseAttempted`         | both    | any write through the reply surface                                                                      |
| `commandsDeployed`          | both    | a command deploy finishes                                                                                |
| `anyInteraction`            | both    | an interaction arrives, before routing                                                                   |
| `unhandledEventError`       | gateway | an event dispatch throws past the boundary                                                               |
| `eventDispatching`          | gateway | before the handlers for an event run                                                                     |
| `eventDispatched`           | gateway | those handlers finish                                                                                    |

{/* prettier-ignore-end */}

> **Gateway and http differ**
>
> `anyInteraction` carries a discord.js `Interaction` on gateway and an `APIInteraction` on http. If a subscriber calls a discord.js method on it, that subscriber compiles on gateway and fails to compile on http.
>
> On http, seedcord publishes it only after the request passes Ed25519 verification and the replay checks. A forged or repeated request never reaches it.

## After a deploy

seedcord logs every deploy and fills [`Commands`](https://seedcord.org/guide/commands/mentions/) with each command's mention, so your handlers don't need this key. `commandsDeployed` is for sending your commands somewhere outside the bot, like a bot listing site such as Top.gg that shows your command list. It fires after every deploy, including the redeploy that follows a hot reload.

```ts title="src/subscribers/ListCommands.ts"
import { Subscribe, Subscriber } from '@seedcord/gateway';

@Subscribe('commandsDeployed')
export class ListCommands extends Subscriber<'commandsDeployed'> {
    public async execute(): Promise<void> {
        // topgg is a client from Top.gg's own SDK
        await topgg.postCommands(this.data.global);
    }
}
```

`global` and the values in `guilds` are `APIApplicationCommand[]` straight from Discord, so you can pass them on as they are. A guild command only exists in its own guild, which is why `ListCommands` sends `global` alone.

## Watching everything come in

A trace of everything your bot receives helps when a command doesn't seem to run at all. `anyInteraction` fires before seedcord routes an interaction anywhere. `eventDispatching` fires for client events, ahead of the handlers.

```ts title="src/subscribers/Trace.ts"
@Subscribe('eventDispatching')
export class Trace extends Subscriber<'eventDispatching'> {
    public async execute(): Promise<void> {
        const { name, args } = this.data;

        this.logger.debug(
            `${name} arrived with ${args.length} args`
        );
    }
}
```

`Trace` reads only `args.length`, which works for any event. `name` and `args` narrow together, so a check like `name === 'messageCreate'` gives you that event's own tuple in `args`.

> **Warning**
>
> `eventDispatching` fires only when at least one handler is about to run. It doesn't fire for an event that no handler registered, or for an event whose handlers were all `once` and have already run. It fires before your middleware runs, so if a middleware stops the event, this key has already been published.

`eventDispatched` fires once the handlers finish. Its fields, including each handler's outcome, are on [the telemetry page](https://seedcord.org/guide/events/telemetry/).

## The two unhandled errors

A throw from your handlers, gates, or middleware reaches the fault boundary, which catches and reports it. `unhandledInteractionError` and `unhandledEventError` cover a throw the boundary never sees, like one inside seedcord's own dispatch code ([report it](https://github.com/seedcord/seedcord/issues/new?template=bug_report.yml)). Both carry only `{ error: Error }`. Subscribe to them to hear about those failures in production, since your handlers' own reporting never sees them.

## The fault keys

These two keys carry a fault out of your bot, to a log service or a Discord channel. `unknownException` fires on a raw throw.

{/* prettier-ignore-start */}

| field        | type               | what it holds                                                                           |
| ------------ | ------------------ | --------------------------------------------------------------------------------------- |
| `uuid`       | `UUID`             | this fault, and the id the error card shows the user                                    |
| `dispatchId` | `string \| null`   | the dispatch it came from, null on a process-level throw such as an unhandled rejection |
| `error`      | `Error`            | the throw itself                                                                        |
| `origin`     | `string`           | where it came from, for example `slash:ban` or `event:messageCreate:AutoMod`            |
| `guild`      | `{ id, name }`     | optional, the guild the dispatch ran in                                                 |
| `user`       | `{ id, username }` | optional, whoever ran it                                                                |
| `metadata`   | `unknown`          | optional. On an event fault it holds the event name, the handler, and the args          |

{/* prettier-ignore-end */}

A reported `Notice` thrown from an autocomplete also publishes here, since [`FaultSource`](https://seedcord.org/docs/packages/core/latest/types/fault-source) only has shapes for interactions and events. Check `error` with `instanceof Notice` if your subscriber treats the two differently.

`handledException` fires on a [`Notice`](https://seedcord.org/docs/packages/core/latest/classes/notice) with `report` true.

{/* prettier-ignore-start */}

| field        | type                                                                               | what it holds                                                                                         |
| ------------ | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `uuid`       | `UUID`                                                                             | this fault, and the id the error card shows the user                                                  |
| `dispatchId` | `string`                                                                           | the dispatch that reported it                                                                         |
| `origin`     | `string`                                                                           | the same shape `unknownException` carries                                                             |
| `denial`     | [`Notice`](https://seedcord.org/docs/packages/core/latest/classes/notice)          | the Notice itself                                                                                     |
| `source`     | [`FaultSource`](https://seedcord.org/docs/packages/core/latest/types/fault-source) | where the throw came from, with `kind` of `interaction` or `event` (http only produces `interaction`) |

{/* prettier-ignore-end */}

An event `origin` carries a third segment naming the handler, since one dispatch runs several of them.

seedcord ships a subscriber for each of these keys and registers it for you. Each one [posts a card to a Discord webhook](https://seedcord.org/guide/replying/reporting/) once you set its environment variable.

```txt title=".env"
UNKNOWN_EXCEPTION_WEBHOOK_URL=https://discord.com/api/webhooks/...
HANDLED_EXCEPTION_WEBHOOK_URL=https://discord.com/api/webhooks/...
```

If a variable isn't set, that reporter stays off. seedcord logs a warning about it at startup.
