Skip to main content

Exception Handling

Gracefully handle errors with @BotExceptionHandler.

@BotExceptionHandler

Catch specific exceptions in a handler:

@BotExceptionHandler(IllegalArgumentException.class)
public String handleValidationError(IllegalArgumentException e) {
return "Validation failed: " + e.getMessage();
}

@BotExceptionHandler(SQLException.class)
public String handleDatabaseError(SQLException e) {
log.error("Database error", e);
return "Sorry, a database error occurred.";
}

Scope: The containing @BotController class only.

Multiple Exception Handlers

@BotController
public class MyBot {

@BotExceptionHandler(IllegalArgumentException.class)
public String handleValidation(IllegalArgumentException e) {
return "Invalid input: " + e.getMessage();
}

@BotExceptionHandler(NullPointerException.class)
public String handleNull(NullPointerException e) {
return "Null error occurred";
}

@BotExceptionHandler(Exception.class)
public String handleGeneric(Exception e) {
return "An error occurred";
}
}

Most specific exception handler is tried first. If no match, generic handlers catch it.

@BotControllerAdvice

Global exception handling across all @BotController classes:

@BotControllerAdvice
public class GlobalExceptionHandler {

@BotExceptionHandler(UnauthorizedException.class)
public String handleUnauthorized(UnauthorizedException e) {
return "You don't have permission!";
}

@BotExceptionHandler(Exception.class)
public String handleAll(Exception e) {
log.error("Unhandled exception", e);
return "Sorry, an error occurred!";
}
}

Optional scope by packages, types, or annotations:

// Scope to specific packages
@BotControllerAdvice(basePackages = "com.example.payment")
public class PaymentExceptionHandler { ... }

// Scope to specific classes
@BotControllerAdvice(assignableTypes = PaymentController.class)
public class PaymentHandler { ... }

// Scope to classes with specific annotation
@BotControllerAdvice(annotations = RequiresAuth.class)
public class AuthHandler { ... }

Exception Resolution Order

  1. Most specific handler in the same @BotController
  2. Generic handler in the same @BotController
  3. Most specific handler in @BotControllerAdvice (matching scope)
  4. Generic handler in @BotControllerAdvice
  5. Default error handling (sends generic message)

Example: Complete Error Handling

@BotController
public class OrderBot {

@Autowired
private OrderService orderService;

@BotCommand("/order")
public String placeOrder(@BotCommandQueryParam("id") String orderId) {
Order order = orderService.getOrder(orderId); // May throw OrderNotFoundException
return "Order: " + order.getName() + ", Price: $" + order.getPrice();
}

@BotExceptionHandler(OrderNotFoundException.class)
public String handleOrderNotFound(OrderNotFoundException e) {
return "Order not found: " + e.getOrderId();
}

@BotExceptionHandler(IllegalArgumentException.class)
public String handleValidation(IllegalArgumentException e) {
return "Invalid order ID format";
}
}

@BotControllerAdvice
public class GlobalHandlers {

@BotExceptionHandler(DatabaseException.class)
public String handleDatabase(DatabaseException e) {
log.error("Database error", e);
return "Database error - please try again later";
}

@BotExceptionHandler(Exception.class)
public String handleGeneric(Exception e) {
log.error("Unexpected error", e);
return "Sorry, an unexpected error occurred!";
}
}

Logging Errors

@BotExceptionHandler(Exception.class)
public String handleError(Exception e) {
log.error("Handler failed", e); // Log before responding
return "An error occurred. Please try again.";
}

Testing Exception Handlers

@SpringBootTest
public class ErrorHandlingTest {

@Autowired
private MyBot bot;

@Test
public void testExceptionHandling() {
assertThrows(
IllegalArgumentException.class,
() -> bot.processOrder(null)
);

String response = bot.handleValidation(
new IllegalArgumentException("Invalid")
);

assertThat(response).contains("Validation failed");
}
}

Ready to learn about transports? Check out Transport Guides.