{ "code": "USER_NOT_FOUND", "message": "User not found"}
code is present only when the upstream TypeScript implementation defines a constant for that message; the table mirrors every defineErrorCodes entry in better-auth and its plugins. Messages without an upstream code carry only message, so clients should branch on code when it exists and fall back to the status otherwise.
For 5xx errors, the actual error details are hidden and a generic message is returned to avoid leaking internal information.
use better_auth::error::{AuthError, AuthResult};fn handle_result(result: AuthResult<()>) { match result { Ok(()) => println!("Success"), Err(e) => { println!("Status: {}", e.status_code()); println!("Message: {}", e); } }}
Constructors for common errors:
AuthError::bad_request("Invalid input");AuthError::forbidden("Not allowed");AuthError::not_found("User not found");AuthError::conflict("Email already exists");AuthError::internal("Unexpected error");AuthError::validation("Password too short");AuthError::plugin("my-plugin", "Something went wrong");AuthError::not_implemented("Feature not available yet");
When using the Axum integration, AuthError is automatically converted to an HTTP response:
use axum::Json;use better_auth::error::AuthError;async fn my_handler() -> Result<Json<serde_json::Value>, AuthError> { // AuthError automatically becomes the right HTTP status + JSON body Err(AuthError::bad_request("Something went wrong"))}