Skip to main content

ApiError

Struct ApiError 

Source
pub struct ApiError {
    status: StatusCode,
    body: ErrorBody,
    headers: Vec<(HeaderName, HeaderValue)>,
}
Expand description

A failure rendered as an HTTP status plus an ErrorBody.

Fields§

§status: StatusCode§body: ErrorBody§headers: Vec<(HeaderName, HeaderValue)>

Extra response headers, such as WWW-Authenticate or DPoP-Nonce.

A client’s next move after AuthenticationRequired, ExpiredToken and InvalidToken differs — ask for a credential, refresh, or give up — and a header is how it learns which without parsing the message. Empty for almost every error this crate raises, which is why it is a field rather than a second constructor for every variant above.

Implementations§

Source§

impl ApiError

Source

pub fn new( status: StatusCode, error: impl Into<String>, message: impl Into<String>, ) -> Self

Builds an error response from its parts.

Source

pub fn with_header(self, name: HeaderName, value: impl Into<String>) -> Self

Attaches a response header, such as WWW-Authenticate.

Source

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

A 400 for a body that did not parse or did not make sense.

Source

pub fn repo_not_found(repo: &str) -> Self

No repository here, under either a DID or a handle.

RepoNotFound is spelled as atproto’s own personal data server spells it. The com.atproto.repo.* lexicons declare no error for it — only RecordNotFound and InvalidSwap — but every com.atproto.sync.* lexicon that can miss a repository declares this name, and the reference implementation raises it from describeRepo, deleteRecord and putRecord alike. Inventing a second spelling for the same condition is the failure this constructor exists to avoid.

404 rather than the reference implementation’s 400. XRPC clients switch on the name and not the status, the name is the one they know, and “not found” is what the status field is for.

Source

pub fn repo_mismatch(authenticated: &str, repo: &str) -> Self

The authenticated agent is not the one the repository belongs to.

RepoMismatch, its own name rather than a reused one: an agent token is real and unexpired — ApiError::new with AuthenticationRequired, ExpiredToken or InvalidToken is what a route answers before this is ever reached — and the caller needs to know that what failed is whose repository this is, which no retry of the same credential against the same repository can fix. plan/auth-types.md treats “an agent never writes another agent’s repository” as an invariant rather than a policy knob, and this is where it is enforced: routes::write_record and its siblings compare the DID auth::require_agent_token resolved against the repository the request names, before any write reaches the registry.

Source

pub fn account_mismatch(authenticated: &str, target: &str) -> Self

The authenticated agent is not the account this admin route names.

AccountMismatch, its own name for the same reason Self::repo_mismatch has one: an agent token is real and unexpired, and what failed is whose account this is. Every account-lifecycle mutation is self-service — an agent’s own credential, checked against the account the request names, is the whole of what authorizes them; see plan/auth-types.md. This is what that credential gets for naming somebody else’s account.

Source

pub fn rate_limited(message: impl Into<String>) -> Self

A caller has exceeded a rate limit; see crate::rate_limit.

RateLimitExceeded names no error the lexicons declare — the same position MethodNotImplemented’s own doc note (routes::unknown_route) describes for a route that does not exist at all. 429 is the standard HTTP status for this and every client is expected to already know it even when the name is new to it.

Source

pub fn account_cap_reached(cap: u64) -> Self

This deployment already holds as many accounts as its operator has configured room for.

Its own name rather than Halted: that name says a stop was thrown, by an operator or by this server when its operator’s record is missing, and neither is true of a full deployment — an account leaving, or an operator raising the cap, both clear this without anyone having pulled anything. 503, the same posture ProvisionError::WriteQueueFull takes: a statement about the deployment, not about the caller.

Source

pub fn not_ready(route: &str, snapshot: &LifecycleSnapshot) -> Self

This deployment has not finished being born, or has lost its public standing, and the route asked for is one it therefore defers.

503 with Retry-After, and an error name — ServerNotReady — no lexicon declares, the same position RateLimitExceeded above is in. The status and the header are what a caller actually reads, and both are standard.

§What a relay is meant to conclude

Three conditions a crawler has to tell apart, and this is exactly one of them:

  • Try later — this. 503, a Retry-After, a named reason. The server is fine and is not finished. Retry on the hint.
  • Broken — a refused connection, a 502 from something in front, or a 500 with no Retry-After. Nothing emits a 500 for this condition, which is what makes the absence of the header meaningful.
  • Nothing here — 200 with an empty list or stream. Fine, and holds nothing. Nothing to retry.

Never a 404: “this method does not exist” and “this method is not ready” are facts a relay reacts to completely differently, and only one of them is worth ever asking about again.

The message names the state, which for an unclaimed server is the whole answer: nobody is currently answerable for this deployment, and the fix is to run didbot operate.

Source

pub fn record_not_found(uri: &str) -> Self

The repository is here and the record is not.

Declared by com.atproto.repo.getRecord, which is the one route that returns it: deleteRecord is specified to succeed when the record was already absent.

Source

pub fn status(&self) -> StatusCode

The status this error responds with.

Source

pub fn error(&self) -> &str

The machine-readable error name.

Source

pub fn message(&self) -> &str

The human-readable message.

Trait Implementations§

Source§

impl Clone for ApiError

Source§

fn clone(&self) -> ApiError

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · 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 From<&ProvisionError> for ApiError

Source§

fn from(err: &ProvisionError) -> Self

Maps a provisioning failure onto a status and an error name.

The Display text of the underlying error becomes the message. Every error in the chain is written for the caller to read: a secret, a signing key and the path to a file this deployment keeps are each kept out of it, and a store refusal carries only the sizes and the sentence that are about the request itself.

Source§

impl From<Halted> for ApiError

The name a request gets when the e-stop has halted it.

Halted rather than a generic MethodNotImplemented or an ordinary 503: atproto clients that do not know the name still see a clear “unavailable”, and plan/e-stop.md’s own requirement is that the ones that do know it can tell a halt apart from every other reason a request might fail. The message says whose latch it is, because that decides whether waiting helps: an operator’s stop stays until the operator releases it, and this server’s own pause lifts when the operator’s record is back. Either way, retrying before then does not.

Source§

fn from(halted: Halted) -> Self

Converts to this type from the input type.
Source§

impl IntoResponse for ApiError

Source§

fn into_response(self) -> Response

Create a response.
Source§

impl PartialEq for ApiError

Source§

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

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · 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 Eq for ApiError

Source§

impl StructuralPartialEq for ApiError

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
§

impl<'a, T, E> AsTaggedExplicit<'a, E> for T
where T: 'a,

§

fn explicit(self, class: Class, tag: u32) -> TaggedParser<'a, Explicit, Self, E>

§

impl<'a, T, E> AsTaggedImplicit<'a, E> for T
where T: 'a,

§

fn implicit( self, class: Class, constructed: bool, tag: u32, ) -> TaggedParser<'a, Implicit, Self, E>

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
§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

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

§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
§

impl<T, S> Handler<IntoResponseHandler, S> for T
where T: IntoResponse + Clone + Send + Sync + 'static,

§

type Future = Ready<Response<Body>>

The type of future calling this handler returns.
§

fn call( self, _req: Request<Body>, _state: S, ) -> <T as Handler<IntoResponseHandler, S>>::Future

Call the handler with the given request.
§

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>>,

Apply a [tower::Layer] to the handler. Read more
§

fn with_state(self, state: S) -> HandlerService<Self, T, S>

Convert the handler into a [Service] by providing the state
§

impl<H, T> HandlerWithoutStateExt<T> for H
where H: Handler<T, ()>,

§

fn into_service(self) -> HandlerService<H, T, ()>

Convert the handler into a [Service] and no state.
§

fn into_make_service(self) -> IntoMakeService<HandlerService<H, T, ()>>

Convert the handler into a MakeService and no state. Read more
§

fn into_make_service_with_connect_info<C>( self, ) -> IntoMakeServiceWithConnectInfo<HandlerService<H, T, ()>, C>

Convert the handler into a MakeService which stores information about the incoming connection and has no state. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
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> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
§

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

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
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, 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.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,