# The framework's bus keys

Subscribe to the keys seedcord publishes on the bus. Covers which transport publishes each key, deploys, incoming interactions and events, unhandled errors, and faults.

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://docs.seedcord.org/packages/core/latest/subscribe) or `core.bus.on`, the same way you subscribe to [your own keys](/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://docs.seedcord.org/packages/core/latest/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`](/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](/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://docs.seedcord.org/packages/core/latest/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://docs.seedcord.org/packages/core/latest/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://docs.seedcord.org/packages/core/latest/notice)            | the Notice itself                                                                                     |
| `source`     | [`FaultSource`](https://docs.seedcord.org/packages/core/latest/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](/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.
