Faults
A throw your handler doesn't catch still owes the user an answer. Covers the card every fault renders, anything else you throw, stopping without a reply, and throws outside any handler.
Some calls fail in ways the user can't fix, like a database write. Catch the error and throw a Fault. The user gets a generic card with a tracking id, and the error you caught goes to your fault report along with the command and the user who ran it.
import { Fault, SlashHandler, SlashRoute } from '@seedcord/gateway';
@SlashRoute('ban')
export class Ban extends SlashHandler<'ban'> {
public async execute(): Promise<void> {
const target = this.options.getUser('target');
try {
await cases.open(target.id);
} catch (cause) {
throw new Fault({ cause });
}
await this.reply(`Banned ${target.username}.`);
}
}cause is the error you caught. It reaches your logs and your fault report. The user never sees it.
| you write | reported |
|---|---|
new Fault() | yes |
new Fault({ cause }) | yes, with that error's stack |
new Fault({ report: false }) | no |
All three render the same card.
Letting the error escape shows that card too. The difference is which report it reaches. A Fault goes to handledException, beside the refusals you chose to report. An error nobody caught goes to unknownException. Catch and wrap the calls you expect to fail, so unknownException only shows you the bugs you haven't found yet.
The card every fault renders
Replace the card when you want a different look or an action on it.
seedcord logs that uuid and puts it on the fault report, so a user who pastes it into your support channel gives you the exact log line to search for.
the developer is the default contact. Set notifications.developerUsername to put your own name there.
export const seedcord = new Seedcord({
bot,
subscribers: { path: null },
notifications: {
developerUsername: 'materwelon'
}
});Anything else you throw
Any throw that is neither a Notice nor a Silence renders that same card. That includes a TypeError from reading a property of undefined, a DiscordAPIError from a call you made yourself, and an error thrown inside a library you installed. seedcord logs each one with its uuid and publishes it to the unknownException bus key. Your error's message never reaches the reply.
Two config keys change this. errors.defaultError takes a Notice subclass that seedcord constructs with the uuid, and errors.ignoreApiCodes takes a list of Discord api codes to swallow. Configuring has both in a real config.
Stopping without a reply
Silence stops the handler, and seedcord does not call Discord at all. That fits an event handler, since nothing is waiting on a response.
@RegisterEvent([Events.GuildMemberAdd])
export class Welcome extends EventHandler<Events.GuildMemberAdd> {
public async execute(): Promise<void> {
const [member] = this.event;
const template = await this.template(member.guild.id);
await member.guild.systemChannel?.send(
template.replace('{user}', `${member}`)
);
}
private async template(guildId: string): Promise<string> {
const saved = await settings.welcome(guildId);
if (!saved) throw new Silence('no welcome message set');
return saved;
}
}template() throws from inside a helper, and execute() never checks a return value, the same way a Notice works in an interaction handler.
Gateway only
Only a gateway bot receives client events, so only @seedcord/gateway exports EventHandler. In an event handler, a Notice with report false also stops without a report.
The reason string reaches a debug log and stops there. new Silence() without one skips that log line, and setting errors.logSilences to false skips it for every Silence.
Danger
Throwing a Silence from an interaction handler leaves the user with Discord's own failure message. Before any ack Discord says the application did not respond, and after a defer() the thinking placeholder stays. Throw a Notice there.
Throws outside a handler
seedcord registers an unhandledRejection and an uncaughtException listener while your bot starts. They catch the throws that reach Node itself, like a promise you forgot to await or a setTimeout callback that threw. Each one reports as an unknown fault.
| listener | route id on the report | what happens next |
|---|---|---|
unhandledRejection | process:unhandledRejection | the bot keeps running |
uncaughtException | process:uncaughtException | seedcord runs the shutdown tasks and exits 1 |
Both fire outside any interaction, so nobody sees a reply. When your application registers its own pair, set errors.catchProcessErrors to false, which Configuring shows in a real config.
Http only
An edge bot registers neither listener, since createSeedcord returns a request handler and never starts a host process.
Every reported throw ends up in your logs and on the bus. Reporting faults sends them to a Discord channel you read.