#[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
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).
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.
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
impl RouteError
Sourcepub fn status_and_code(&self) -> (StatusCode, &'static str)
pub fn status_and_code(&self) -> (StatusCode, &'static str)
HTTP status + stable error code for this variant.
Trait Implementations§
Source§impl Debug for RouteError
impl Debug for RouteError
Source§impl Display for RouteError
impl Display for RouteError
Source§impl Error for RouteError
impl Error for RouteError
Source§fn source(&self) -> Option<&(dyn Error + 'static)>
fn source(&self) -> Option<&(dyn Error + 'static)>
1.0.0 · Source§fn description(&self) -> &str
fn description(&self) -> &str
use the Display impl or to_string()
Source§impl From<AppleRedirectError> for RouteError
Available on crate feature apple only.
impl From<AppleRedirectError> for RouteError
apple only.Source§fn from(value: AppleRedirectError) -> Self
fn from(value: AppleRedirectError) -> Self
Source§impl From<AuditValidationError> for RouteError
impl From<AuditValidationError> for RouteError
Source§fn from(source: AuditValidationError) -> Self
fn from(source: AuditValidationError) -> Self
Source§impl From<CampAuthorityError> for RouteError
impl From<CampAuthorityError> for RouteError
Source§fn from(value: CampAuthorityError) -> Self
fn from(value: CampAuthorityError) -> Self
Source§impl From<Error> for RouteError
impl From<Error> for RouteError
Source§impl From<MagicLinkError> for RouteError
Available on crate feature email only.
impl From<MagicLinkError> for RouteError
email only.Source§fn from(value: MagicLinkError) -> Self
fn from(value: MagicLinkError) -> Self
Source§impl From<MailerError> for RouteError
Available on crate feature email only.
impl From<MailerError> for RouteError
email only.Source§fn from(value: MailerError) -> Self
fn from(value: MailerError) -> Self
Source§impl From<OidcError> for RouteError
Available on crate features apple or google only.
impl From<OidcError> for RouteError
apple or google only.Source§impl From<OwnershipValidationError> for RouteError
impl From<OwnershipValidationError> for RouteError
Source§fn from(source: OwnershipValidationError) -> Self
fn from(source: OwnershipValidationError) -> Self
Source§impl From<PasskeyError> for RouteError
Available on crate feature passkey only.
impl From<PasskeyError> for RouteError
passkey only.Source§fn from(value: PasskeyError) -> Self
fn from(value: PasskeyError) -> Self
Source§impl From<RouteError> for KnockRouteError
impl From<RouteError> for KnockRouteError
Source§fn from(source: RouteError) -> Self
fn from(source: RouteError) -> Self
Source§impl From<ServicePrincipalError> for RouteError
impl From<ServicePrincipalError> for RouteError
Source§fn from(value: ServicePrincipalError) -> Self
fn from(value: ServicePrincipalError) -> Self
Source§impl From<StoreError> for RouteError
impl From<StoreError> for RouteError
Source§fn from(value: StoreError) -> Self
fn from(value: StoreError) -> Self
Source§impl IntoResponse for RouteError
impl IntoResponse for RouteError
Source§fn into_response(self) -> Response
fn into_response(self) -> Response
Auto Trait Implementations§
impl Freeze for RouteError
impl RefUnwindSafe for RouteError
impl Send for RouteError
impl Sync for RouteError
impl Unpin for RouteError
impl UnsafeUnpin for RouteError
impl UnwindSafe for RouteError
Blanket Implementations§
Source§impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
Source§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
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