Commands

Handling Errors

With Seyfert, you can handle errors in an organized way and treat them differently depending on the type of error.

Error when executing a command

This is the most common error and occurs when an error is thrown in the run method.

import { Command, type CommandContext } from "seyfert";

export class HandlingErrors extends Command {
  async run(context: CommandContext) {
    throw new Error("Error, ehm, lol player detected");
  }

// : This responds with the previous error message: "Error, ehm, lol player detected"
  async onRunError(context: CommandContext, error: unknown) {
    context.client.logger.fatal(error);
    await context.editOrReply({
      content: error instanceof Error ? error.message : `Error: ${error}`
    });
  }
}

Error when validating options

This error is thrown when an option fails in the value method.

const options = {
  url: createStringOption({
    description: 'how to be a gamer',
    value(data, ok: OKFunction<URL>, fail) {
        if (isURL(data.value)) return ok(new URL(data.value));

// This will trigger the onOptionsError method
        fail('expected a valid URL');
    },
  })
};

@Options(options)
export class HandlingErrors extends Command {
    async onOptionsError(
        context: CommandContext,
        metadata: OnOptionsReturnObject
    ) {
// : url: expected a valid URL
    await context.editOrReply({
      content: Object.entries(metadata)
        .filter((_) => _[1].failed)
        .map((error) => `${error[0]}: ${error[1].value}`)
        .join("\n")
    });
  }
}

Stop a middleware with an error

When a middleware returns a stop, Seyfert generates this error and stops the progress of the command being handled.

import { createMiddleware } from "seyfert";

export default createMiddleware<void>(({ context, next, stop }) => {
    if (!Devs.includes(context.author.id)) {
        return stop("User is not a developer");
    }
    next();
});
import { Command, Middlewares, type CommandContext } from "seyfert";

@Middlewares(["OnlyDev"])
export class HandlingErrors extends Command {
    async onMiddlewaresError(context: CommandContext, error: string) {
        await context.editOrReply({
// : User is not a developer
            content: error
        });
    }
}

Missing member permissions

When a member doesn't have the required permissions specified in defaultMemberPermissions:

import { Command, type CommandContext, type PermissionStrings } from "seyfert";

export class HandlingErrors extends Command {
  async onPermissionsFail(context: CommandContext, permissions: PermissionStrings) {
    await context.editOrReply({
      content: `You need the following permissions: ${permissions.join(', ')}`,
    });
  }
}

Missing bot permissions

When the bot doesn't have the permissions specified in botPermissions:

import { Command, type CommandContext, type PermissionStrings } from "seyfert";

export class HandlingErrors extends Command {
  async onBotPermissionsFail(context: CommandContext, permissions: PermissionStrings) {
    await context.editOrReply({
      content: `I need the following permissions: ${permissions.join(', ')}`,
    });
  }
}

Lifecycle Hooks

Beyond error handlers, Seyfert provides lifecycle hooks for additional control:

import { Command, type CommandContext, type UsingClient } from "seyfert";

export class MyCommand extends Command {
  // Called before middlewares run
  async onBeforeMiddlewares(context: CommandContext) {}

  // Called before options are parsed — useful for early deferral
  async onBeforeOptions(context: CommandContext) {
    await context.deferReply();
  }

  // Called after run() completes (with error if thrown)
  async onAfterRun(context: CommandContext, error?: unknown) {}

  // Called on internal Seyfert errors
  async onInternalError(client: UsingClient, error: unknown) {}
}

Default Error Handlers

Instead of defining error handlers on every command, you can set defaults in the Client constructor:

import { Client } from "seyfert";

const client = new Client({
  commands: {
    defaults: {
      onRunError: (context, error) => {
        context.editOrReply({ content: 'Something went wrong!' });
      },
      onOptionsError: (context) => {
        context.editOrReply({ content: 'Invalid options provided.' });
      },
      onPermissionsFail: (context, permissions) => {
        context.editOrReply({ content: `Missing permissions: ${permissions.join(', ')}` });
      },
      onBotPermissionsFail: (context, permissions) => {
        context.editOrReply({ content: `I need: ${permissions.join(', ')}` });
      },
      onMiddlewaresError: (context, error) => {
        context.editOrReply({ content: error });
      },
      onInternalError: (client, error) => {
        client.logger.fatal(error);
      },
    },
  },
  // Defaults for component handlers
  components: {
    defaults: {
      onRunError: (context, error) => {
        context.editOrReply({ content: 'Component error!' });
      },
    },
  },
  // Defaults for modal handlers
  modals: {
    defaults: {
      onRunError: (context, error) => {
        context.editOrReply({ content: 'Modal error!' });
      },
    },
  },
});

Per-command error handlers override the defaults. If a command defines its own onRunError, the default won't be called for that command.