Skip to main content

Handling errors

The execution of a interaction may fail for many reasons. You can handle these errors in a way that is adapted to each type of errors, this is done via the InteractionErrorHandler interface.

Global error handler

You simply need to create a class implementing InteractionErrorHandler. If you are using the Botrino framework, it will automatically be set into the interaction service. Otherwise, you need to set it via the builder when constructing your InteractionService:

final var interactionService = InteractionService.builder(config, gateway)
.setErrorHandler(new MyErrorHandler())
.build();

Here is an example of InteractionErrorHandler implementation:

package testbot1;

import botrino.api.util.DurationUtils;
import botrino.interaction.InteractionErrorHandler;
import botrino.interaction.InteractionFailedException;
import botrino.interaction.context.InteractionContext;
import botrino.interaction.cooldown.CooldownException;
import botrino.interaction.privilege.PrivilegeException;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono;

public final class MyErrorHandler implements InteractionErrorHandler {

@Override
public Publisher<?> handleInteractionFailed(InteractionFailedException e, InteractionContext ctx) {
return ctx.event().createFollowup("\uD83D\uDEAB " + e.getMessage()).withEphemeral(true);
}

@Override
public Publisher<?> handlePrivilege(PrivilegeException e, InteractionContext ctx) {
return ctx.event().createFollowup("You have insufficient privileges " +
"to run this command.").withEphemeral(true);
}

@Override
public Publisher<?> handleCooldown(CooldownException e, InteractionContext ctx) {
return ctx.event().createFollowup("You are on cooldown. " +
"Retry in " + DurationUtils.format(e.getRetryAfter())).withEphemeral(true);
}

@Override
public Publisher<?> handleDefault(Throwable t, InteractionContext ctx) {
return ctx.event().createFollowup("Something went wrong! Sorry for the inconvenience.")
.withEphemeral(true)
.onErrorResume(e -> {
t.addSuppressed(e);
return Mono.empty();
}).then(Mono.error(t)); // Forward downstream for logging
}
}

The methods of InteractionErrorHandler correspond to the most common error types. Each method exposes the InteractionContext in which the error happened. None of them are required to be implemented, by default they just forward errors downstream which will only log them at ERROR level. Currently there are four of them:

  • handleInteractionFailed(InteractionFailedException, InteractionContext): allows recovering on a InteractionFailedException. This exception represents a "normal" failure of the command, when the end user is at fault. This exception is generally thrown by yourself in your run() method and carries a user-friendly message, so the way to handle it will mostly consist of replying to the user with that message.
  • handlePrivilege(PrivilegeException, CommandContext): allows recovering on a PrivilegeException. It is thrown when a user attempts to use a command with insufficient privileges. Typically, handling this exception will consist of telling the user they cannot use the command. More details on privileges can be found in this section.
  • handleCooldown(CooldownException, CommandContext): allows recovering on a CooldownException. It is thrown when a user attempts to use a command past the maximum usage limit within a time interval. Generally, it will be handled by notifying the user that they need to wait some time before trying the command again (the exception carries the exact time left). More details on cooldowns can be found in this section.
  • handleDefault(Throwable, CommandContext): allows recovering on an exception type that corresponds to none of the above.
Considerations when using the Botrino framework
  • The implementation class must have a no-arg constructor.
  • If more than one implementation of InteractionErrorHandler are found, it will result in an error as it is impossible to determine which one to use. If you don't want to remove the extra implementation(s), you can mark one of them with the @Primary annotation to lift the ambiguity. You may alternatively use the @Exclude annotation if you don't want one implementation to be picked up by Botrino.