Skip to content

Error Handling

IntoResponse trait

IntoResponse is defined in src/error/mod.rs:

pub trait IntoResponse {
fn into_response(self) -> Response;
}

Implement this on your error enum so handlers can return Result<Response, MyError> and convert the error to an HTTP response with .unwrap_or_else(|e| e.into_response()).

Response itself implements IntoResponse as the identity conversion, so it can be used wherever impl IntoResponse is expected.

AppError enum

AppError is a built-in typed error that covers the most common HTTP failure cases:

#[derive(Debug, PartialEq, Eq)]
pub enum AppError {
BadRequest(String), // 400
Unauthorized, // 401
Forbidden, // 403
NotFound(String), // 404
Conflict(String), // 409
UnprocessableEntity(String), // 422
TooManyRequests, // 429
Internal(String), // 500
}

Each variant’s into_response() produces a plain-text body from the attached message, with the appropriate status code and a standard header set.

Returning Result from handlers

Handlers registered on AppWithState must return Response, not Result. Use .unwrap_or_else to fold the error into the response at the call site:

use rust_web_server::error::{AppError, IntoResponse};
use rust_web_server::response::Response;
use rust_web_server::request::Request;
use rust_web_server::router::PathParams;
use rust_web_server::server::ConnectionInfo;
fn find_user(id: u64) -> Result<Response, AppError> {
if id == 0 {
return Err(AppError::NotFound("user not found".to_string()));
}
// ... real lookup
Err(AppError::Internal("db connection failed".to_string()))
}
fn get_user(
_req: &Request,
params: &PathParams,
_conn: &ConnectionInfo,
_state: &(),
) -> Response {
let id: u64 = params
.get("id")
.and_then(|s| s.parse().ok())
.unwrap_or(0);
find_user(id).unwrap_or_else(|e| e.into_response())
}

Custom error type

Implement IntoResponse on your own error enum when AppError does not map cleanly to your domain:

use rust_web_server::error::IntoResponse;
use rust_web_server::response::{Response, STATUS_CODE_REASON_PHRASE};
use rust_web_server::header::Header;
use rust_web_server::range::Range;
use rust_web_server::mime_type::MimeType;
#[derive(Debug)]
pub enum ApiError {
Validation(String),
DatabaseError(String),
NotAuthenticated,
}
impl IntoResponse for ApiError {
fn into_response(self) -> Response {
let (status, message) = match &self {
ApiError::Validation(msg) => (STATUS_CODE_REASON_PHRASE.n422_unprocessable_entity, msg.as_str()),
ApiError::DatabaseError(msg) => (STATUS_CODE_REASON_PHRASE.n500_internal_server_error, msg.as_str()),
ApiError::NotAuthenticated => (STATUS_CODE_REASON_PHRASE.n401_unauthorized, "Not authenticated"),
};
// Build a minimal fake request so Header::get_header_list has something to work from.
use rust_web_server::request::Request;
let dummy = Request {
method: "GET".to_string(),
request_uri: "/".to_string(),
http_version: "HTTP/1.1".to_string(),
headers: vec![],
body: vec![],
};
let headers = Header::get_header_list(&dummy);
let body = Range::get_content_range(
message.as_bytes().to_vec(),
MimeType::TEXT_PLAIN.to_string(),
);
Response::get_response(status, Some(headers), Some(vec![body]))
}
}

Then use it in handlers exactly like AppError:

fn handler(...) -> Response {
do_something().unwrap_or_else(|e: ApiError| e.into_response())
}

AppError variant reference

VariantStatusBody
BadRequest(msg)400msg
Unauthorized401"Unauthorized"
Forbidden403"Forbidden"
NotFound(msg)404msg
Conflict(msg)409msg
UnprocessableEntity(msg)422msg
TooManyRequests429"Too Many Requests"
Internal(msg)500msg