Cross-service gRPC error framework for Rust.
aerro gives every error a typed identity, a bounded call trace, and a
structured wire encoding — so the client that receives a tonic::Status can
recover the original variant, read the chain of service hops it passed through,
and decide whether to surface the message to the end user.
- Typed errors — derive
Aerroon any enum; each variant carries a category, gRPC status code, and structured message template - Bounded call traces — every hop appends a
Frame; frames are elided to a configurable cap on the wire so large fan-outs stay bounded - Exposure control —
Internal,Trusted, andPublictiers redact system errors and strip call traces automatically at the egress point - Zero allocations on the happy path — no heap work when there is no error
Add aerro to your Cargo.toml:
[dependencies]
aerro = "0.9"The macro feature — which provides #[derive(Aerro)] — is enabled by default,
so no extra dependency or feature flag is needed.
Define your errors, encode them into a tonic::Status, and recover them on the
other side:
use aerro::{Aerro, IntoStatus, StatusIntoResultExt};
use aerro::wire::encode::EncodeOptions;
#[derive(Debug, aerro::Aerro)]
pub enum CreateUserError {
#[aerro(category = Business, code = AlreadyExists, error = "email already taken: {email}")]
EmailTaken { email: String },
#[aerro(category = System, code = Internal, error = "db.unavailable")]
DbUnavailable,
}
// Server side — convert a typed failure to a tonic::Status.
let err = CreateUserError::EmailTaken { email: "alice@example.com".into() };
let status = err.into_status(&EncodeOptions::default());
// Client side — recover the original typed variant.
let recovered = status.into_aerro::<CreateUserError>().unwrap();| Example | What it shows |
|---|---|
basic |
Minimum viable usage — one enum, one wire round-trip |
trace_chain |
3-hop trace accumulation across service boundaries |
exposure |
Internal / Trusted / Public redaction tiers |
Run any example with:
cargo run --example basic --features macro
| Flag | Default | Description |
|---|---|---|
macro |
✓ | #[derive(Aerro)] proc-macro (enabled by default — no extra config needed) |
tracing |
✗ | Capture OTel trace/span IDs from the active tracing span |
Alpha. The core wire format and derive macros are functional and the API is stabilising, but has not yet been used in production. Expect minor breaking changes in the 0.x series.
Feedback, issues, and PRs are welcome.
Licensed under either of:
at your option.