Errors
Athena has three error families plus the CommandError discriminator used by the commands framework.
Athena has three error families plus the CommandError discriminator used by the commands framework.
DiscordRESTError
Thrown when Discord returns a structured JSON error (4xx with a code and message). Carries Discord's numeric code.
import { DiscordRESTError } from 'athena';
try {
await client.createMessage(channelID, '...');
} catch (err) {
if (err instanceof DiscordRESTError) {
console.error(err.code, err.message); // e.g. 50001 Missing Access
}
}Properties: code (Discord error code), message (flattened, including field-level errors), response (parsed body), req, res, headers, name (DiscordRESTError [code]).
DiscordHTTPError
Thrown for transport-level or non-JSON failures (a 502 after retries, a CloudFlare HTML page). err.code is the HTTP status.
CommandError
The commands framework does not throw on command failures; it emits a commandError event with a discriminated payload.
import { CommandError } from 'athena';
client.on('commandError', (command, interaction, error) => {
switch (error.type) {
case CommandError.NotDeveloperGuild: break; // error.data: guild ID
case CommandError.DeniedUser: break; // error.data: user ID
case CommandError.DeniedGuild: break; // error.data: guild ID
case CommandError.MissingPermission: break; // error.data: { userID, permissions }
case CommandError.OnCooldown: break; // error.data: { userID, cooldown }
case CommandError.MiddlewareError: break; // error.data: the thrown value
case CommandError.UncaughtError: break; // error.data: Error; error.interaction: which handler
}
});A reusable reporter:
client.on('commandError', async (command, interaction, error) => {
try {
if (error.type === CommandError.OnCooldown) {
const secs = Math.max(0, Math.ceil(((error.data.cooldown.expiry ?? 0) - Date.now()) / 1000));
return await interaction.createMessage({ content: `On cooldown for ${secs}s.`, flags: MessageFlags.Ephemeral });
}
if (error.type === CommandError.MissingPermission) {
return await interaction.createMessage({ content: `You need: ${error.data.permissions.join(', ')}`, flags: MessageFlags.Ephemeral });
}
if (error.type === CommandError.UncaughtError) {
logger.error({ command, err: error.data });
return await interaction.createMessage({ content: 'Something went wrong.', flags: MessageFlags.Ephemeral });
}
} catch {
// the interaction may already be answered; ignore follow-up failures
}
});Common Discord codes
| Code | Meaning |
|---|---|
| 10003 | Unknown channel |
| 10008 | Unknown message |
| 10062 | Unknown interaction (you responded too late, the 3 second window) |
| 40060 | Interaction already acknowledged |
| 50001 | Missing access |
| 50013 | Missing permissions |
| 50035 | Invalid form body |
Full list: https://discord.com/developers/docs/topics/opcodes-and-status-codes#json.
Tip
10062 and 40060 almost always mean a timing or double-ack bug: respond once, within three seconds, and defer() first if the work takes longer.