1Error handling model and utilities
2
3# Table of contents
4
5- [axum's error handling model](#axums-error-handling-model)
6- [Routing to fallible services](#routing-to-fallible-services)
7- [Applying fallible middleware](#applying-fallible-middleware)
8- [Running extractors for error handling](#running-extractors-for-error-handling)
9
10# axum's error handling model
11
12axum is based on [`tower::Service`] which bundles errors through its associated
13`Error` type. If you have a [`Service`] that produces an error and that error
14makes it all the way up to hyper, the connection will be terminated _without_
15sending a response. This is generally not desirable so axum makes sure you
16always produce a response by relying on the type system.
17
18axum does this by requiring all services have [`Infallible`] as their error
19type. `Infallible` is the error type for errors that can never happen.
20
21This means if you define a handler like:
22
23```rust
24use axum::http::StatusCode;
25
26async fn handler() -> Result<String, StatusCode> {
27    # todo!()
28    // ...
29}
30```
31
32While it looks like it might fail with a `StatusCode` this actually isn't an
33"error". If this handler returns `Err(some_status_code)` that will still be
34converted into a [`Response`] and sent back to the client. This is done
35through `StatusCode`'s [`IntoResponse`] implementation.
36
37It doesn't matter whether you return `Err(StatusCode::NOT_FOUND)` or
38`Err(StatusCode::INTERNAL_SERVER_ERROR)`. These are not considered errors in
39axum.
40
41Instead of a direct `StatusCode`, it makes sense to use intermediate error type
42that can ultimately be converted to `Response`. This allows using `?` operator
43in handlers. See those examples:
44
45* [`anyhow-error-response`][anyhow] for generic boxed errors
46* [`error-handling-and-dependency-injection`][ehdi] for application-specific detailed errors
47
48[anyhow]: https://github.com/tokio-rs/axum/blob/main/examples/anyhow-error-response/src/main.rs
49[ehdi]: https://github.com/tokio-rs/axum/blob/main/examples/error-handling-and-dependency-injection/src/main.rs
50
51This also applies to extractors. If an extractor doesn't match the request the
52request will be rejected and a response will be returned without calling your
53handler. See [`extract`](crate::extract) to learn more about handling extractor
54failures.
55
56# Routing to fallible services
57
58You generally don't have to think about errors if you're only using async
59functions as handlers. However if you're embedding general `Service`s or
60applying middleware, which might produce errors you have to tell axum how to
61convert those errors into responses.
62
63```rust
64use axum::{
65    Router,
66    body::Body,
67    http::{Request, Response, StatusCode},
68    error_handling::HandleError,
69};
70
71async fn thing_that_might_fail() -> Result<(), anyhow::Error> {
72    # Ok(())
73    // ...
74}
75
76// this service might fail with `anyhow::Error`
77let some_fallible_service = tower::service_fn(|_req| async {
78    thing_that_might_fail().await?;
79    Ok::<_, anyhow::Error>(Response::new(Body::empty()))
80});
81
82let app = Router::new().route_service(
83    "/",
84    // we cannot route to `some_fallible_service` directly since it might fail.
85    // we have to use `handle_error` which converts its errors into responses
86    // and changes its error type from `anyhow::Error` to `Infallible`.
87    HandleError::new(some_fallible_service, handle_anyhow_error),
88);
89
90// handle errors by converting them into something that implements
91// `IntoResponse`
92async fn handle_anyhow_error(err: anyhow::Error) -> (StatusCode, String) {
93    (
94        StatusCode::INTERNAL_SERVER_ERROR,
95        format!("Something went wrong: {}", err),
96    )
97}
98# async {
99# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
100# };
101```
102
103# Applying fallible middleware
104
105Similarly axum requires you to handle errors from middleware. That is done with
106[`HandleErrorLayer`]:
107
108```rust
109use axum::{
110    Router,
111    BoxError,
112    routing::get,
113    http::StatusCode,
114    error_handling::HandleErrorLayer,
115};
116use std::time::Duration;
117use tower::ServiceBuilder;
118
119let app = Router::new()
120    .route("/", get(|| async {}))
121    .layer(
122        ServiceBuilder::new()
123            // `timeout` will produce an error if the handler takes
124            // too long so we must handle those
125            .layer(HandleErrorLayer::new(handle_timeout_error))
126            .timeout(Duration::from_secs(30))
127    );
128
129async fn handle_timeout_error(err: BoxError) -> (StatusCode, String) {
130    if err.is::<tower::timeout::error::Elapsed>() {
131        (
132            StatusCode::REQUEST_TIMEOUT,
133            "Request took too long".to_string(),
134        )
135    } else {
136        (
137            StatusCode::INTERNAL_SERVER_ERROR,
138            format!("Unhandled internal error: {}", err),
139        )
140    }
141}
142# async {
143# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
144# };
145```
146
147# Running extractors for error handling
148
149`HandleErrorLayer` also supports running extractors:
150
151```rust
152use axum::{
153    Router,
154    BoxError,
155    routing::get,
156    http::{StatusCode, Method, Uri},
157    error_handling::HandleErrorLayer,
158};
159use std::time::Duration;
160use tower::ServiceBuilder;
161
162let app = Router::new()
163    .route("/", get(|| async {}))
164    .layer(
165        ServiceBuilder::new()
166            // `timeout` will produce an error if the handler takes
167            // too long so we must handle those
168            .layer(HandleErrorLayer::new(handle_timeout_error))
169            .timeout(Duration::from_secs(30))
170    );
171
172async fn handle_timeout_error(
173    // `Method` and `Uri` are extractors so they can be used here
174    method: Method,
175    uri: Uri,
176    // the last argument must be the error itself
177    err: BoxError,
178) -> (StatusCode, String) {
179    (
180        StatusCode::INTERNAL_SERVER_ERROR,
181        format!("`{} {}` failed with {}", method, uri, err),
182    )
183}
184# async {
185# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
186# };
187```
188
189[`tower::Service`]: `tower::Service`
190[`Infallible`]: std::convert::Infallible
191[`Response`]: crate::response::Response
192[`IntoResponse`]: crate::response::IntoResponse
193