If the status code of Response is an error, and the Body of Response is empty, then salvo will try to use Catcher to catch the error and display a friendly error page.

A simple way to create a custom Catcher is to return a system default Catcher via Catcher::default(), and then add it to Service.

use salvo::catcher::Catcher;


The default Catcher supports sending error pages in XML, JSON, HTML, Text formats.

You can add a custom error handler to Catcher by adding hoop to the default Catcher. The error handler is still Handler.

You can add multiple custom error catching handlers to Catcher through hoop. The custom error handler can call the FlowCtrl::skip_next method after handling the error to skip next error handlers and return early.

use salvo::catcher::Catcher;
use salvo::prelude::*;

async fn hello() -> &'static str {
    "Hello World"
async fn error500(res: &mut Response) {

async fn main() {

    let acceptor = TcpListener::new("").bind().await;

fn create_service() -> Service {
    let router = Router::new()

async fn handle404(res: &mut Response, ctrl: &mut FlowCtrl) {
    if let Some(StatusCode::NOT_FOUND) = res.status_code {
        res.render("Custom 404 Error Page");
name = "example-custom-error-page"
version = "0.1.0"
edition = "2021"
publish = false

salvo.workspace = true
tokio = { version = "1", features = ["macros"] }
tracing = "0.1"
tracing-subscriber = "0.3"