masterror · Framework-agnostic application error types
Small, pragmatic error model for API-heavy Rust services.
Core is framework-agnostic; integrations are opt-in via feature flags.
Stable categories, conservative HTTP mapping, no unsafe
.
- Core types:
AppError
,AppErrorKind
,AppResult
,AppCode
,ErrorResponse
- Optional Axum/Actix integration
- Optional OpenAPI schema (via
utoipa
) - Conversions from
sqlx
,reqwest
,redis
,validator
,config
,tokio
TL;DR
[]
= { = "0.3", = false }
# or with features:
# masterror = { version = "0.3", features = [
# "axum", "actix", "serde_json", "openapi",
# "sqlx", "reqwest", "redis", "validator", "config", "tokio"
# ] }
Since v0.3.0: stable AppCode
enum and extended ErrorResponse
with retry/authentication metadata.
- Stable taxonomy. Small set of
AppErrorKind
categories mapping conservatively to HTTP. - Framework-agnostic. No assumptions, no
unsafe
, MSRV pinned. - Opt-in integrations. Zero default features; you enable what you need.
- Clean wire contract.
ErrorResponse { status, code, message, details?, retry?, www_authenticate? }
. - One log at boundary. Log once with
tracing
. - Less boilerplate. Built-in conversions, compact prelude.
- Consistent workspace. Same error surface across crates.
[]
# lean core
= { = "0.3", = false }
# with Axum/Actix + JSON + integrations
# masterror = { version = "0.3", features = [
# "axum", "actix", "serde_json", "openapi",
# "sqlx", "reqwest", "redis", "validator", "config", "tokio"
# ] }
MSRV: 1.89
No unsafe: forbidden by crate.
Create an error:
use ;
let err = new;
assert!;
With prelude:
use *;
use ;
let app_err = new;
let resp: ErrorResponse = .into
.with_retry_after_secs
.with_www_authenticate;
assert_eq!;
// features = ["axum", "serde_json"]
use ;
use ;
async
let app = new.route;
// features = ["actix", "serde_json"]
use ;
use *;
async
async
[]
= { = "0.3", = ["openapi", "serde_json"] }
= "5"
axum
— IntoResponseactix
— ResponseError/Responderopenapi
— utoipa schemaserde_json
— JSON detailssqlx
,redis
,reqwest
,validator
,config
,tokio
,multipart
std::io::Error
→ InternalString
→ BadRequestsqlx::Error
→ NotFound/Databaseredis::RedisError
→ Servicereqwest::Error
→ Timeout/Network/ExternalApivalidator::ValidationErrors
→ Validationconfig::ConfigError
→ Configtokio::time::error::Elapsed
→ Timeout
Minimal core:
= { = "0.3", = false }
API (Axum + JSON + deps):
= { = "0.3", = [
"axum", "serde_json", "openapi",
"sqlx", "reqwest", "redis", "validator", "config", "tokio"
] }
API (Actix + JSON + deps):
= { = "0.3", = [
"actix", "serde_json", "openapi",
"sqlx", "reqwest", "redis", "validator", "config", "tokio"
] }
- Use
ErrorResponse::new(status, AppCode::..., "msg")
instead of legacy - New helpers:
.with_retry_after_secs
,.with_www_authenticate
ErrorResponse::new_legacy
is temporary shim
Semantic versioning. Breaking API/wire contract → major bump.
MSRV = 1.89 (may raise in minor, never in patch).
- Not a general-purpose error aggregator like
anyhow
- Not a replacement for your domain errors
Apache-2.0 OR MIT, at your option.