pub struct Error { /* private fields */ }Expand description
The primary error type for the modo framework.
Error carries:
- an HTTP
StatusCodethat will be used as the response status, - a human-readable
messagestring, - an optional structured
detailspayload (arbitrary JSON), - an optional boxed
sourceerror for causal chaining, - an optional static
error_codestring that survives the response pipeline, - an optional static
locale_keythat lets the default error handler translate the message at response-build time, - a
laggedflag used by the SSE broadcaster to signal that a subscriber dropped messages.
§Conversion to HTTP response
Calling into_response() produces a JSON body:
{ "error": { "status": 404, "message": "user not found" } }If with_details was called, a "details" field is included.
A copy of the error (without source) is also stored in response extensions so middleware
can inspect it after the fact.
§Clone behaviour
Cloning an Error drops the source field because Box<dyn Error> is not Clone.
The error_code, locale_key, details, and all other fields are preserved.
Implementations§
Source§impl Error
impl Error
Sourcepub fn new(status: StatusCode, message: impl Into<String>) -> Self
pub fn new(status: StatusCode, message: impl Into<String>) -> Self
Create a new error with the given HTTP status code and message.
Prefer one of the named status-code constructors
(Error::not_found, Error::bad_request, Error::internal, …)
when they match. Use new only for statuses without a dedicated
constructor.
§Example
use modo::error::Error;
use modo::axum::http::StatusCode;
let err = Error::new(StatusCode::IM_A_TEAPOT, "no coffee here");
assert_eq!(err.status(), StatusCode::IM_A_TEAPOT);Sourcepub fn with_source(
status: StatusCode,
message: impl Into<String>,
source: impl Error + Send + Sync + 'static,
) -> Self
pub fn with_source( status: StatusCode, message: impl Into<String>, source: impl Error + Send + Sync + 'static, ) -> Self
Create a new error with a status code, message, and a boxed source error.
with_source is a constructor, not a builder method — it wraps an
underlying error at construction time. When you already have an
Error and want to attach a cause, use the chain
builder instead.
§Example
use modo::error::Error;
use modo::axum::http::StatusCode;
use std::io;
let io_err = io::Error::new(io::ErrorKind::NotFound, "missing");
let err = Error::with_source(StatusCode::INTERNAL_SERVER_ERROR, "read failed", io_err);
assert!(err.source_as::<io::Error>().is_some());Sourcepub fn localized(status: StatusCode, key: &'static str) -> Self
pub fn localized(status: StatusCode, key: &'static str) -> Self
Create an error whose message is a translation key.
The key is stored in the locale_key slot and is also used as the raw
message. When the
default_error_handler runs
and a Translator is present in the request
extensions (installed by
I18nLayer), it resolves key into the
user-facing string at response-build time. Without that middleware (or
without a Translator), the response falls back to the raw key — making
the behaviour predictable and easy to spot in logs.
This constructor leaves error_code, details, and source unset;
chain with_code,
with_details, or chain
afterwards if needed.
§Kwargs and logging
Translation kwargs ({count}, {name}, etc.) are not yet supported at
the Error level — the default handler calls tr.t(key, &[]) with no
arguments. When you need interpolation, attach a descriptive fallback
message via Error::with_locale_key and run translation (with kwargs)
inside a custom handler passed to
error_handler.
Also note that Debug and Display print the raw key (because the
fallback message is the key), which makes structured logs look like
errors.user.not_found rather than human text. Prefer
Error::with_locale_key when you want log-friendly output alongside
the translation tag.
Sourcepub fn status(&self) -> StatusCode
pub fn status(&self) -> StatusCode
Returns the HTTP status code of this error.
Sourcepub fn with_details(self, details: Value) -> Self
pub fn with_details(self, details: Value) -> Self
Attach a structured JSON details payload (builder-style).
The payload is rendered under the "error.details" key in the JSON
response body produced by Error::into_response.
§Example
use modo::error::Error;
use modo::serde_json::json;
let err = Error::unprocessable_entity("validation failed")
.with_details(json!({ "field": "email", "reason": "invalid format" }));
assert!(err.details().is_some());Sourcepub fn chain(self, source: impl Error + Send + Sync + 'static) -> Self
pub fn chain(self, source: impl Error + Send + Sync + 'static) -> Self
Attach a source error (builder-style).
The source is stored in a Box<dyn std::error::Error + Send + Sync>
and can be downcast with Error::source_as while you still own the
Error. Note: the source is dropped on Clone and on
IntoResponse::into_response — pair .chain(src) with
.with_code(code) when you need identity that
survives the response boundary.
§Example
use modo::error::Error;
use std::io;
let err = Error::internal("disk write failed")
.chain(io::Error::other("no space"));
assert!(err.source_as::<io::Error>().is_some());Sourcepub fn with_code(self, code: &'static str) -> Self
pub fn with_code(self, code: &'static str) -> Self
Attach a static error code to preserve error identity through the response pipeline.
The source field is dropped on Clone and on
Error::into_response, so downstream middleware reading the error
copy from response extensions cannot recover the original cause. A
static error_code survives both boundaries and is the canonical way
to identify an error post-response.
This is a builder method: the existing message, status, locale_key,
details, and source fields are preserved — only error_code is
replaced.
§Example
use modo::error::Error;
use axum::response::IntoResponse;
let err = Error::unauthorized("token expired").with_code("jwt:expired");
let resp = err.into_response();
let ext = resp.extensions().get::<Error>().unwrap();
assert_eq!(ext.error_code(), Some("jwt:expired"));Sourcepub fn error_code(&self) -> Option<&'static str>
pub fn error_code(&self) -> Option<&'static str>
Returns the error code, if one was set.
Sourcepub fn with_locale_key(self, key: &'static str) -> Self
pub fn with_locale_key(self, key: &'static str) -> Self
Tag an existing error with a translation key (builder-style).
Unlike Error::localized, this preserves the current message — use
it when you already have a descriptive fallback string and want to add a
translation key alongside it. The
default_error_handler will
prefer the translated value whenever a
Translator is available in the request
extensions, and otherwise keep the stored message untouched.
This is a builder method: the existing message, status, error_code,
details, and source fields are preserved — only locale_key is
replaced.
Sourcepub fn locale_key(&self) -> Option<&'static str>
pub fn locale_key(&self) -> Option<&'static str>
Returns the translation key, if one was set via Error::localized or
Error::with_locale_key.
Sourcepub fn source_as<T: Error + 'static>(&self) -> Option<&T>
pub fn source_as<T: Error + 'static>(&self) -> Option<&T>
Downcast the source error to a concrete type.
Returns None if no source is set or if the source is not of type T.
Sourcepub fn bad_request(msg: impl Into<String>) -> Self
pub fn bad_request(msg: impl Into<String>) -> Self
Create a 400 Bad Request error.
Create a 401 Unauthorized error.
Sourcepub fn payload_too_large(msg: impl Into<String>) -> Self
pub fn payload_too_large(msg: impl Into<String>) -> Self
Create a 413 Payload Too Large error.
Sourcepub fn unprocessable_entity(msg: impl Into<String>) -> Self
pub fn unprocessable_entity(msg: impl Into<String>) -> Self
Create a 422 Unprocessable Entity error.
Sourcepub fn too_many_requests(msg: impl Into<String>) -> Self
pub fn too_many_requests(msg: impl Into<String>) -> Self
Create a 429 Too Many Requests error.
Sourcepub fn bad_gateway(msg: impl Into<String>) -> Self
pub fn bad_gateway(msg: impl Into<String>) -> Self
Create a 502 Bad Gateway error.
Sourcepub fn gateway_timeout(msg: impl Into<String>) -> Self
pub fn gateway_timeout(msg: impl Into<String>) -> Self
Create a 504 Gateway Timeout error.
Trait Implementations§
Source§impl Clone for Error
Clones the error, dropping the source field (which is not Clone).
impl Clone for Error
Clones the error, dropping the source field (which is not Clone).
All other fields — status, message, error_code, locale_key, details, and
lagged — are preserved.
Source§impl Error for Error
impl Error for Error
Source§fn source(&self) -> Option<&(dyn Error + 'static)>
fn source(&self) -> Option<&(dyn Error + 'static)>
1.0.0 · Source§fn description(&self) -> &str
fn description(&self) -> &str
use the Display impl or to_string()
Source§impl From<Error> for Error
Converts libsql::Error into modo::Error with
appropriate HTTP status codes:
impl From<Error> for Error
Converts libsql::Error into modo::Error with
appropriate HTTP status codes:
- Unique/primary-key constraint violations become
409 Conflict. - Foreign-key violations become
400 Bad Request. QueryReturnedNoRowsbecomes404 Not Found.- Everything else becomes
500 Internal Server Error.
Source§impl From<ValidationError> for Error
impl From<ValidationError> for Error
Source§fn from(ve: ValidationError) -> Self
fn from(ve: ValidationError) -> Self
Source§impl IntoResponse for Error
Converts Error into an axum Response.
impl IntoResponse for Error
Converts Error into an axum Response.
Produces a JSON body of the form:
{ "error": { "status": 422, "message": "validation failed" } }If with_details was called, a "details" key is added under "error".
A copy of the error (without the source field) is stored in response extensions under
the type Error so that downstream middleware can inspect it.
Source§fn into_response(self) -> Response
fn into_response(self) -> Response
Auto Trait Implementations§
impl Freeze for Error
impl !RefUnwindSafe for Error
impl Send for Error
impl Sync for Error
impl Unpin for Error
impl UnsafeUnpin for Error
impl !UnwindSafe for Error
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T, S> Handler<IntoResponseHandler, S> for T
impl<T, S> Handler<IntoResponseHandler, S> for T
Source§fn call(
self,
_req: Request<Body>,
_state: S,
) -> <T as Handler<IntoResponseHandler, S>>::Future
fn call( self, _req: Request<Body>, _state: S, ) -> <T as Handler<IntoResponseHandler, S>>::Future
Source§fn layer<L>(self, layer: L) -> Layered<L, Self, T, S>where
L: Layer<HandlerService<Self, T, S>> + Clone,
<L as Layer<HandlerService<Self, T, S>>>::Service: Service<Request<Body>>,
fn layer<L>(self, layer: L) -> Layered<L, Self, T, S>where
L: Layer<HandlerService<Self, T, S>> + Clone,
<L as Layer<HandlerService<Self, T, S>>>::Service: Service<Request<Body>>,
tower::Layer to the handler. Read moreSource§fn with_state(self, state: S) -> HandlerService<Self, T, S>
fn with_state(self, state: S) -> HandlerService<Self, T, S>
Service by providing the stateSource§impl<H, T> HandlerWithoutStateExt<T> for H
impl<H, T> HandlerWithoutStateExt<T> for H
Source§fn into_service(self) -> HandlerService<H, T, ()>
fn into_service(self) -> HandlerService<H, T, ()>
Service and no state.Source§fn into_make_service(self) -> IntoMakeService<HandlerService<H, T, ()>>
fn into_make_service(self) -> IntoMakeService<HandlerService<H, T, ()>>
MakeService and no state. Read moreSource§fn into_make_service_with_connect_info<C>(
self,
) -> IntoMakeServiceWithConnectInfo<HandlerService<H, T, ()>, C>
fn into_make_service_with_connect_info<C>( self, ) -> IntoMakeServiceWithConnectInfo<HandlerService<H, T, ()>, C>
MakeService which stores information
about the incoming connection and has no state. Read moreSource§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
Source§impl<T> ToStringFallible for Twhere
T: Display,
impl<T> ToStringFallible for Twhere
T: Display,
Source§fn try_to_string(&self) -> Result<String, TryReserveError>
fn try_to_string(&self) -> Result<String, TryReserveError>
ToString::to_string, but without panic on OOM.