Skip to content

Errors

Errors share one JSON envelope:

{"code":"INVALID_EMAIL_OR_PASSWORD","message":"Invalid email or password"}

code is a stable upper-case identifier you can switch on; message is human-readable and may change. Only errors that have an upstream constant carry a code; a few responses (rate limits, some validation failures) carry only a message. Unexpected failures return an empty 500 — the cause is logged on the server and never sent to the client.

Common codes (the full set per plugin is on its page and in the OpenAPI document):

Status Code Raised by
400 VALIDATION_ERROR Malformed body: {"code":"VALIDATION_ERROR","message":"[body.email] Invalid email address"}
400 PASSWORD_TOO_SHORT, PASSWORD_TOO_LONG Password policy
400 EMAIL_PASSWORD_SIGN_UP_DISABLED Sign-up without enable_signup(true)
400 INVALID_TOKEN Reset, verification, magic-link tokens
400 FIELD_NOT_ALLOWED Writing a read-only field
401 INVALID_EMAIL_OR_PASSWORD Wrong credentials
401 UNAUTHORIZED, Authentication required No valid session
403 MISSING_OR_NULL_ORIGIN, INVALID_ORIGIN, INVALID_CALLBACK_URL Origin and redirect checks
403 EMAIL_NOT_VERIFIED Unverified email at sign-in
403 BANNED_USER Admin ban
403 SESSION_NOT_FRESH Freshness
404 PROVIDER_NOT_FOUND Unknown OAuth provider
422 USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL Duplicate sign-up
413 — Body over BodyLimitConfig::max_bytes
429 — Rate limited (see X-Retry-After)

In Rust, operations return AuthResult<T> = Result<T, AuthError>. Each variant maps to an HTTP status through AuthError::status_code() and renders with to_auth_response().

Variant Status Use
Api { status, code, message } as given An intentional public error from application policy; the message is returned verbatim
Upstream { status, code, message } as given A documented upstream error with a fixed code and message
BadRequest, InvalidRequest, Validation 400 Invalid input
InvalidCredentials, Unauthenticated, AuthenticationFailed, SessionNotFound 401 Authentication failures
Forbidden, Unauthorized, BannedUser 403 Authorization failures
NotFound, UserNotFound 404 Missing resources
Conflict, UnprocessableEntity 409 / 422 Conflicts
PayloadTooLarge, MethodNotAllowed, RateLimited 413 / 405 / 429 Request-level rejections
NotImplemented 501 A feature the configured store or plugin does not support
Config, Database, Internal, Encryption, PasswordHash, Jwt, Serialization, Plugin 500 Internal failures; private details are logged, not returned
CallbackFailure(Box<AuthError>) 500 (empty) An ordinary failure inside an application callback

Helper constructors: AuthError::bad_request(msg), forbidden, not_found, conflict, validation, internal, config, not_implemented.

Delivery and policy callbacks return AuthResult. What the client sees depends on the variant:

use alibi::{AuthError, AuthResult};
fn check(allowed: bool) -> AuthResult<()> {
if !allowed {
// An intentional, public error: rendered with this status and message.
return Err(AuthError::Api {
status: 403,
code: Some("TENANT_SUSPENDED".into()),
message: "This workspace is suspended".into(),
});
}
// Anything else (database, network, bug) becomes an empty 500 and is logged.
Ok(())
}

Return AuthError::Api/forbidden/bad_request for errors the user may see; let infrastructure failures propagate as ordinary errors. Never put secrets in a public message.

AuthError::Database(DatabaseError) wraps storage failures. They are logged and returned as 500. Constraint violations that map to a user-facing condition (a duplicate email, for example) are translated before they reach this variant.