Skip to main content

ApiError

Struct ApiError 

Source
pub struct ApiError {
    pub code: ErrorCode,
    pub title: String,
    pub status: u16,
    pub detail: String,
    pub request_id: Option<Uuid>,
    pub errors: Vec<ValidationError>,
    pub rate_limit: Option<RateLimitInfo>,
    pub source: Option<Arc<dyn Error + Send + Sync + 'static>>,
    pub causes: Vec<Self>,
    pub extensions: BTreeMap<String, Value>,
}
Expand description

RFC 9457 Problem Details error response.

Wire format field mapping:

  • code"type" — URN per RFC 9457 §3.1.1 (e.g. urn:api-bones:error:resource-not-found)
  • title"title" — RFC 9457 §3.1.2
  • status"status" — HTTP status code, RFC 9457 §3.1.3 (valid range: 100–599)
  • detail"detail" — RFC 9457 §3.1.4
  • request_id"instance" — URI per RFC 9457 §3.1.5, as urn:uuid:<uuid> per RFC 4122 §3
  • errors"errors" — documented extension for field-level validation errors

Content-Type must be set to application/problem+json by the HTTP layer.

Requires std or alloc (fields contain String/Vec).

§Examples

use api_bones::error::{ApiError, ErrorCode};

let err = ApiError::new(ErrorCode::ResourceNotFound, "User 42 not found");
assert_eq!(err.status, 404);
assert_eq!(err.detail, "User 42 not found");

Fields§

§code: ErrorCode

Machine-readable error URN (RFC 9457 §3.1.1 type).

§title: String

Human-friendly error label (RFC 9457 §3.1.2 title).

§status: u16

HTTP status code (RFC 9457 §3.1.3 status). Valid range: 100–599.

§detail: String

Human-readable error specifics (RFC 9457 §3.1.4 detail).

§request_id: Option<Uuid>

URI identifying this specific occurrence (RFC 9457 §3.1.5 instance). Serialized as urn:uuid:<uuid> per RFC 4122 §3.

§errors: Vec<ValidationError>

Structured field-level validation errors (extension). Omitted when empty.

§rate_limit: Option<RateLimitInfo>

Structured rate-limit metadata (extension). Present on 429 responses when built via ApiError::rate_limited_with or ApiError::with_rate_limit. Serialized as the top-level rate_limit member.

§source: Option<Arc<dyn Error + Send + Sync + 'static>>

Upstream error that caused this ApiError, if any.

Not serialized — for in-process error chaining only. Exposed via core::error::Error::source so that anyhow, eyre, and tracing can walk the full error chain.

Requires std or alloc (uses Arc).

§causes: Vec<Self>

Nested cause chain serialized as RFC 9457 extension member "causes".

Each entry is a nested Problem Details object. Omitted when empty. Preserved through From conversions.

§extensions: BTreeMap<String, Value>

Arbitrary RFC 9457 extension members attached by the caller.

Serialized inline at the top level of the JSON object (flattened). Keys must not collide with the standard Problem Details fields.

Use ApiError::with_extension to attach values.

Implementations§

Source§

impl ApiError

Source

pub fn new(code: ErrorCode, detail: impl Into<String>) -> Self

Create a new ApiError. title and status are derived from code.

§Examples
use api_bones::error::{ApiError, ErrorCode};

let err = ApiError::new(ErrorCode::BadRequest, "missing field");
assert_eq!(err.status, 400);
assert_eq!(err.title, "Bad Request");
assert_eq!(err.detail, "missing field");
Source

pub fn with_request_id(self, id: Uuid) -> Self

Attach a request ID (typically set by tracing middleware). Serializes as "instance": "urn:uuid:<id>" per RFC 9457 §3.1.5 + RFC 4122 §3.

§Examples
use api_bones::error::{ApiError, ErrorCode};
use uuid::Uuid;

let err = ApiError::new(ErrorCode::BadRequest, "oops")
    .with_request_id(Uuid::nil());
assert_eq!(err.request_id, Some(Uuid::nil()));
Source

pub fn with_errors(self, errors: Vec<ValidationError>) -> Self

Attach structured field-level validation errors.

§Examples
use api_bones::error::{ApiError, ErrorCode, ValidationError};

let err = ApiError::new(ErrorCode::ValidationFailed, "invalid input")
    .with_errors(vec![
        ValidationError { field: "/email".into(), message: "invalid".into(), rule: None },
    ]);
assert_eq!(err.errors.len(), 1);
Source

pub fn with_source(self, source: impl Error + Send + Sync + 'static) -> Self

Attach an upstream error as the source() for this ApiError.

The source is exposed via core::error::Error::source for error-chain tools (anyhow, eyre, tracing) but is not serialized to the wire.

Requires std or alloc (uses Arc).

Source

pub fn with_causes(self, causes: Vec<Self>) -> Self

Attach a chain of nested cause errors, serialized as "causes" in Problem Details output.

§Examples
use api_bones::error::{ApiError, ErrorCode};

let cause = ApiError::not_found("upstream resource missing");
let err = ApiError::internal("pipeline failed")
    .with_causes(vec![cause]);
assert_eq!(err.causes.len(), 1);
Source

pub fn with_extension( self, key: impl Into<String>, value: impl Into<Value>, ) -> Self

Attach a single arbitrary RFC 9457 extension member.

The value is serialized inline (flattened) in the Problem Details object. Requires the serde feature.

§Examples
use api_bones::error::ApiError;

let err = ApiError::internal("boom")
    .with_extension("trace_id", "abc-123");
assert_eq!(err.extensions["trace_id"], "abc-123");
Source

pub fn status_code(&self) -> u16

HTTP status code.

Source

pub fn is_client_error(&self) -> bool

Whether this is a client error (4xx).

§Examples
use api_bones::error::{ApiError, ErrorCode};

assert!(ApiError::not_found("gone").is_client_error());
assert!(!ApiError::internal("boom").is_client_error());
Source

pub fn is_server_error(&self) -> bool

Whether this is a server error (5xx).

§Examples
use api_bones::error::{ApiError, ErrorCode};

assert!(ApiError::internal("boom").is_server_error());
assert!(!ApiError::not_found("gone").is_server_error());
Source

pub fn bad_request(msg: impl Into<String>) -> Self

400 Bad Request.

§Examples
use api_bones::error::ApiError;

let err = ApiError::bad_request("missing param");
assert_eq!(err.status, 400);
assert_eq!(err.title, "Bad Request");
Source

pub fn validation_failed(msg: impl Into<String>) -> Self

400 Validation Failed.

Source

pub fn unauthorized(msg: impl Into<String>) -> Self

401 Unauthorized.

Source

pub fn invalid_credentials() -> Self

401 Invalid Credentials.

Source

pub fn token_expired() -> Self

401 Token Expired.

Source

pub fn forbidden(msg: impl Into<String>) -> Self

403 Forbidden.

Source

pub fn insufficient_permissions(msg: impl Into<String>) -> Self

403 Insufficient Permissions.

Source

pub fn not_found(msg: impl Into<String>) -> Self

404 Resource Not Found.

§Examples
use api_bones::error::ApiError;

let err = ApiError::not_found("user 42 not found");
assert_eq!(err.status, 404);
assert_eq!(err.title, "Resource Not Found");
Source

pub fn conflict(msg: impl Into<String>) -> Self

409 Conflict.

Source

pub fn already_exists(msg: impl Into<String>) -> Self

409 Resource Already Exists.

Source

pub fn unprocessable(msg: impl Into<String>) -> Self

422 Unprocessable Entity.

Source

pub fn rate_limited(retry_after_seconds: u64) -> Self

429 Rate Limited.

Source

pub fn with_rate_limit(self, info: RateLimitInfo) -> Self

Attach structured RateLimitInfo metadata. Serialized as the top-level rate_limit member and propagated to ProblemJson as an extension of the same name.

§Examples
use api_bones::error::ApiError;
use api_bones::ratelimit::RateLimitInfo;

let info = RateLimitInfo::new(100, 0, 1_700_000_000).retry_after(60);
let err = ApiError::rate_limited(60).with_rate_limit(info.clone());
assert_eq!(err.rate_limit.as_ref(), Some(&info));
Source

pub fn rate_limited_with(info: RateLimitInfo) -> Self

429 Rate Limited with structured quota metadata.

Convenience over ApiError::rate_limited + ApiError::with_rate_limit. The detail string is derived from info.retry_after when set.

§Examples
use api_bones::error::ApiError;
use api_bones::ratelimit::RateLimitInfo;

let info = RateLimitInfo::new(100, 0, 1_700_000_000).retry_after(30);
let err = ApiError::rate_limited_with(info);
assert_eq!(err.status, 429);
assert!(err.rate_limit.is_some());
Source

pub fn internal(msg: impl Into<String>) -> Self

500 Internal Server Error. Never expose internal details in msg.

Source

pub fn unavailable(msg: impl Into<String>) -> Self

503 Service Unavailable.

Source

pub fn builder() -> ApiErrorBuilder<(), ()>

Return a typed builder for constructing an ApiError.

Required fields (code and detail) must be set before calling ApiErrorBuilder::build; the compiler enforces this via typestate.

§Example
use api_bones::error::{ApiError, ErrorCode};

let err = ApiError::builder()
    .code(ErrorCode::ResourceNotFound)
    .detail("Booking 123 not found")
    .build();
assert_eq!(err.status, 404);

Trait Implementations§

Source§

impl Clone for ApiError

Source§

fn clone(&self) -> ApiError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ApiError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for ApiError

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Display for ApiError

Available on crate features std or alloc only.
Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ApiError

Available on crate features std or alloc only.
Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0:

use the Display impl or to_string()

1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0:

replaced by Error::source, which can support downcasting

Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<ApiError> for ProblemJson

Available on crate features std and serde only.
Source§

fn from(err: ApiError) -> Self

Convert an ApiError into a ProblemJson.

  • codetype via ErrorCode::urn
  • request_id (UUID) → instance as "urn:uuid:<id>"
  • errors (validation) → "errors" extension member
  • source is dropped (not part of the wire format)
§Examples
use api_bones::error::{ApiError, ErrorCode, ProblemJson};

let err = ApiError::new(ErrorCode::Forbidden, "not allowed");
let p = ProblemJson::from(err);
assert_eq!(p.status, 403);
assert_eq!(p.title, "Forbidden");
Source§

impl<E: HttpError> From<E> for ApiError

Available on crate features std or alloc only.

Blanket conversion: any HttpError implementor becomes an ApiError.

This is a blanket impl over a sealed trait parameter so it does not conflict with other From impls on ApiError.

Source§

fn from(e: E) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ApiError

Available on crate features std or alloc only.
Source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Serialize for ApiError

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> ValidateIp for T
where T: ToString,

Source§

fn validate_ipv4(&self) -> bool

Validates whether the given string is an IP V4
Source§

fn validate_ipv6(&self) -> bool

Validates whether the given string is an IP V6
Source§

fn validate_ip(&self) -> bool

Validates whether the given string is an IP
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,