Authsome

Errors

Sentinel error values, API error response format, and error handling patterns.

Authsome defines sentinel error values in dedicated packages. Use errors.Is to check for specific error conditions. Never compare error strings directly — error messages are not part of the public API.

Using errors.Is

import (
    "errors"

    "github.com/xraph/authsome/account"
    "github.com/xraph/authsome/store"
)

u, sess, err := engine.SignIn(ctx, req)
switch {
case errors.Is(err, account.ErrInvalidCredentials):
    // Wrong email or password — return 401
case errors.Is(err, account.ErrAccountLocked):
    // Too many failed attempts — return 423
case errors.Is(err, account.ErrUserBanned):
    // User is banned — return 403
case err != nil:
    // Unexpected error — return 500
default:
    // Success
}

All service methods wrap sentinel errors using fmt.Errorf and %w, so errors.Is works correctly even when the error is wrapped with additional context.

Store errors

Package: github.com/xraph/authsome/store

ErrorDescription
store.ErrNotFoundThe requested entity does not exist. Returned by all Get/Lookup operations.
store.ErrDuplicateA unique constraint was violated (e.g., duplicate email).
store.ErrConflictA write conflict or optimistic lock violation occurred.
u, err := engine.Store().GetUserByEmail(ctx, appID, email)
if errors.Is(err, store.ErrNotFound) {
    // User does not exist
}

Account errors

Package: github.com/xraph/authsome/account

Authentication errors

ErrorDescription
account.ErrInvalidCredentialsEmail/password combination is incorrect, or the identifier was not found. Deliberately vague to prevent email enumeration.
account.ErrUserBannedThe user's account has been banned (permanently or temporarily).
account.ErrAccountLockedThe account has been locked due to too many failed sign-in attempts.
account.ErrSessionExpiredThe access token or refresh token has expired.

Password errors

ErrorDescription
account.ErrPasswordBreachedThe password was found in the HaveIBeenPwned breach database. Only returned when CheckBreached: true.
account.ErrPasswordReusedThe new password matches one of the user's previous passwords. Only returned when HistoryCount > 0.
account.ErrPasswordExpiredThe user's password has exceeded MaxAgeDays. Returned from SignIn — the user must change their password.
account.ErrPasswordTooWeakThe password does not meet the policy (min length, uppercase, digit, special).

Registration errors

ErrorDescription
account.ErrEmailTakenA user with this email already exists in the app.
account.ErrUsernameTakenA user with this username already exists in the app.

API error response format

When a request fails, the HTTP API returns a JSON error body with a consistent shape:

{
  "error": {
    "code": "invalid_credentials",
    "message": "Invalid email or password.",
    "status": 401
  }
}

HTTP status code mapping

Sentinel errorHTTP statusError code
account.ErrInvalidCredentials401 Unauthorizedinvalid_credentials
account.ErrSessionExpired401 Unauthorizedsession_expired
account.ErrUserBanned403 Forbiddenuser_banned
account.ErrAccountLocked423 Lockedaccount_locked
account.ErrEmailTaken409 Conflictemail_taken
account.ErrUsernameTaken409 Conflictusername_taken
account.ErrPasswordBreached422 Unprocessable Entitypassword_breached
account.ErrPasswordReused422 Unprocessable Entitypassword_reused
account.ErrPasswordExpired428 Precondition Requiredpassword_expired
account.ErrPasswordTooWeak422 Unprocessable Entitypassword_too_weak
store.ErrNotFound404 Not Foundnot_found
Any unexpected error500 Internal Server Errorinternal_error

Error wrapping patterns

Authsome wraps all internal errors with context before returning them. The wrapping format is consistent: "authsome: <operation>: <underlying>".

// Internal wrapping — always use errors.Is, not string comparison.
if err := e.store.CreateUser(ctx, u); err != nil {
    return nil, nil, fmt.Errorf("authsome: create user: %w", err)
}

When you receive an error from the engine, unwrap it to inspect the root cause:

u, sess, err := engine.SignUp(ctx, req)
if err != nil {
    // Check for the specific root cause.
    if errors.Is(err, account.ErrEmailTaken) {
        // handle duplicate email
        return
    }
    // Log the full wrapped error for debugging.
    slog.Error("signup failed", "error", err)
}

Plugin errors

Plugin OnBeforeSignUp and OnBeforeSignIn hooks can return custom errors to block the operation. Return any error value — it will be wrapped and propagated to the caller:

func (p *DomainRestrictPlugin) OnBeforeSignUp(_ context.Context, req *account.SignUpRequest) error {
    if !strings.HasSuffix(req.Email, "@mycompany.com") {
        return fmt.Errorf("only @mycompany.com emails are allowed")
    }
    return nil
}

The caller receives: "authsome: before signup: only @mycompany.com emails are allowed".

Form validation errors

When a FormConfig is active and the sign-up request fails field validation, the engine returns a descriptive error message:

// Returns: "authsome: form validation: company_name is required"
u, sess, err := engine.SignUp(ctx, req)

Individual field validation errors are checked before the user is created. The error message includes the field name to help the client surface appropriate UI feedback.

On this page