Skip to main content

RouteError

Enum RouteError 

Source
#[non_exhaustive]
pub enum RouteError {
Show 38 variants MissingCsrfCookie, CsrfStateMismatch, UnknownFlow, FlowExpired, Provider(String), IdToken(String), Upstream(String), Store(String), Config(String), MalformedCallback(String), Ceremony(String), UnknownCredential, AlreadyUsed, InvalidEmail, WrongPurpose(String), MagicLinkToken(String), MailerBuild(String), MailerTransport(String), MissingBearer, MalformedBearer, Unauthorized, UnknownDevice, UnknownToken, UnentitledScopes(String), NotEntitledForAud(String), InvalidTokenRequest(String), InsufficientScope { required: Scope, }, OwnershipInvalid(OwnershipValidationError), InvalidOwnershipQuery(String), UnknownOwnership, GrantForbidden, AuditInvalid(AuditValidationError), AuditSubjectForbidden, InvalidAuditQuery(String), AlreadyExists(String), UnknownPrincipal(String), NotOperator, InvalidDelegation(String),
}
Expand description

What can go wrong inside a cheers-axum route handler.

Each variant maps to a stable JSON body + HTTP status — see RouteError::status_and_code.

Variants (Non-exhaustive)§

This enum is marked as non-exhaustive
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

MissingCsrfCookie

No CSRF cookie on the callback request.

§

CsrfStateMismatch

CSRF cookie present but its value doesn’t match the ?state= param. Either a CSRF attempt or a cross-tab race.

§

UnknownFlow

The OIDC flow keyed on ?state= was never stashed (or already consumed). Most likely a replay of an old callback URL.

§

FlowExpired

The OIDC flow stored at begin() exceeded its TTL before the callback arrived.

§

Provider(String)

The IdP signaled an error in the callback (error=access_denied, …).

§

IdToken(String)

id_token verification failed (signature, audience, nonce, expiry).

§

Upstream(String)

Network / HTTP failure talking to the IdP’s token endpoint.

§

Store(String)

Backend store failure — UserStore, RefreshStore, or RevocationWriter returned an error.

§

Config(String)

Catch-all for misconfiguration the handler can’t continue past (missing token endpoint, wrong client_secret shape, …).

§

MalformedCallback(String)

Apple’s form-post body was missing a required field.

§

Ceremony(String)

A passkey registration/authentication ceremony failed — bad challenge, signature, origin, or excluded credential. The client may retry from a fresh start_*.

§

UnknownCredential

No stored passkey matched the asserted credential id — either the user has no registered passkeys or the assertion came from a credential they do not own. The login must be rejected.

§

AlreadyUsed

Magic-link token replayed after consumption.

§

InvalidEmail

Magic-link request received a malformed email address.

§

WrongPurpose(String)

Magic-link token decoded but its purpose claim was unexpected — a session token presented to a magic-link verify slot, or vice versa.

§

MagicLinkToken(String)

Magic-link token failed PASETO decryption / signature / expiry checks.

§

MailerBuild(String)

Mailer rejected the message (bad address shape, body encoding, …) — typically a deploy-time bug.

§

MailerTransport(String)

Mailer transport failed (SMTP error, network error, auth refused) — may be transient; retry policy is the caller’s choice.

§

MissingBearer

Authorization header absent on a route that needs a bearer token.

§

MalformedBearer

Authorization header present but not in Bearer <token> form (wrong scheme, non-ASCII bytes, empty token).

§

Unauthorized

Bearer token failed verification at the edge — bad signature, expired, or revoked. Collapsed into one variant on purpose: leaking which of the three a token tripped is a small but real oracle.

§

UnknownDevice

DELETE /me/sessions/{device_id} targeted a device the user does not own. 404 so a probe can’t enumerate other users’ device ids.

§

UnknownToken

DELETE /me/tokens/{id} targeted a token id the user does not own, or that does not exist. 404, and the two are deliberately the same answer — same reason UnknownDevice is: a probe must not be able to enumerate other users’ token ids.

§

UnentitledScopes(String)

POST /me/tokens asked for scopes the caller does not currently hold for the requested audience. 400, naming every unheld scope — a token silently narrower than the one requested is a debugging trap that surfaces days later as an unexplained 403.

§

NotEntitledForAud(String)

POST /me/tokens named an audience the caller holds no grant for at all (composition rule (5)). 403 — the caller is authenticated, they simply have no entitlement to attenuate from, so there is no token that could be minted. Distinct from UnentitledScopes, which is “you hold something here, just not that”.

§

InvalidTokenRequest(String)

POST /me/tokens body parsed as JSON but was invalid: empty name, a scope string outside the closed vocabulary, or an expires_in_secs outside the policy range. 400 — distinct from 401/403 so a caller can tell a bad request from an auth failure.

§

InsufficientScope

An MCP-call token authenticated but lacks a scope the handler requires (e.g. camp:read on a camp route). 403 — the principal is known, the request is just not authorized.

Fields

§required: Scope
§

OwnershipInvalid(OwnershipValidationError)

POST /ownership body parsed as JSON but violated a NewOwnership invariant (e.g. on_behalf_of named a non-user principal). 400 — distinct from 401/403 so a caller can tell auth failure from a well-formed-but-invalid row.

§

InvalidOwnershipQuery(String)

GET /ownership named neither a resource nor a subject, only part of one, or both forms at once. 400.

§

UnknownOwnership

DELETE /ownership/{id} targeted an id no OwnershipStore row matches. 404 — the absence is reported, not the cause.

§

GrantForbidden

The caller holds no relation on the resource whose grants include the relation it tried to write, revoke, or (for a list) any relation at all — the D4 grant door’s refusal. 403: the caller is known.

§

AuditInvalid(AuditValidationError)

POST /audit/ingest body parsed as JSON but a record violated an AuditRecord invariant (e.g. empty aud, non-positive at). 400 — distinct from 401/403 so kamaji can tell auth failure from a well-formed-but-invalid batch (do not retry as-is).

§

AuditSubjectForbidden

GET /audit/by-on-behalf-of/{user} was addressed to a principal the caller may not read: a user asking for a different user’s history, or a principal kind with no defined audit subject (camp). 403 — the caller authenticated and holds audit:read, it just may not read this subject. Distinct from InsufficientScope, which is about the token lacking the scope at all.

§

InvalidAuditQuery(String)

GET /audit/by-on-behalf-of/{user} carried a malformed path principal, a non-user principal, or a cursor that didn’t decode. 400 — well-formed-but-invalid input the caller can fix without re-authenticating (a stale cursor is fixed by dropping it and restarting the page walk).

§

AlreadyExists(String)

POST /admin/service-principals collided with an existing principal id. 409 — same shape as a database unique-key conflict.

§

UnknownPrincipal(String)

POST /admin/service-principals/{id}/rotate (or similar) named a principal cheers doesn’t know. 404.

§

NotOperator

An authenticated session bearer reached an admin endpoint, but the authenticated user is not on the product’s operator list. 403 — the caller proved who they are, they’re just not authorized for this surface. Distinct from InsufficientScope (which is the MCP/scope equivalent) so admin endpoints can stay scope-free without conflating the two.

§

InvalidDelegation(String)

POST /admin/camps/bootstrap rejected a user-signed delegation for reasons distinct from auth failure: the delegation’s bound_to / camp_id did not match the provision request, it had already expired (expires_at <= now), or it failed an invariant. 400 — well-formed-but-invalid input, callable to fix without re-auth.

Implementations§

Source§

impl RouteError

Source

pub fn status_and_code(&self) -> (StatusCode, &'static str)

HTTP status + stable error code for this variant.

Trait Implementations§

Source§

impl Debug for RouteError

Source§

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

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

impl Display for RouteError

Source§

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

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

impl Error for RouteError

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<AppleRedirectError> for RouteError

Available on crate feature apple only.
Source§

fn from(value: AppleRedirectError) -> Self

Converts to this type from the input type.
Source§

impl From<AuditValidationError> for RouteError

Source§

fn from(source: AuditValidationError) -> Self

Converts to this type from the input type.
Source§

impl From<CampAuthorityError> for RouteError

Source§

fn from(value: CampAuthorityError) -> Self

Converts to this type from the input type.
Source§

impl From<Error> for RouteError

Source§

fn from(value: Error) -> Self

Converts to this type from the input type.
Source§

impl From<MagicLinkError> for RouteError

Available on crate feature email only.
Source§

fn from(value: MagicLinkError) -> Self

Converts to this type from the input type.
Source§

impl From<MailerError> for RouteError

Available on crate feature email only.
Source§

fn from(value: MailerError) -> Self

Converts to this type from the input type.
Source§

impl From<OidcError> for RouteError

Available on crate features apple or google only.
Source§

fn from(value: OidcError) -> Self

Converts to this type from the input type.
Source§

impl From<OwnershipValidationError> for RouteError

Source§

fn from(source: OwnershipValidationError) -> Self

Converts to this type from the input type.
Source§

impl From<PasskeyError> for RouteError

Available on crate feature passkey only.
Source§

fn from(value: PasskeyError) -> Self

Converts to this type from the input type.
Source§

impl From<RouteError> for KnockRouteError

Source§

fn from(source: RouteError) -> Self

Converts to this type from the input type.
Source§

impl From<ServicePrincipalError> for RouteError

Source§

fn from(value: ServicePrincipalError) -> Self

Converts to this type from the input type.
Source§

impl From<StoreError> for RouteError

Source§

fn from(value: StoreError) -> Self

Converts to this type from the input type.
Source§

impl IntoResponse for RouteError

Source§

fn into_response(self) -> Response

Create a response.

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<'a, T, E> AsTaggedExplicit<'a, E> for T
where T: 'a,

Source§

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

Source§

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

Source§

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> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,

Source§

impl<T> Instrument for T

Source§

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

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

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
Source§

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

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + 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
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + 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> 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 = !

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

fn try_from(value: U) -> Result<T, !>

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<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

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
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

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