Error codes

Every error code the Voult API returns, what it means, and what to do about it.

How errors arrive

JSON responses (every /api/* route):

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

Branch on code. message is for humans and can change between releases; status repeats the HTTP status.

@voult/sdk throws these as typed errors with the same code and status. @voult/express passes them through to the browser as JSON, and @voult/react throws them as VoultRequestError.

Hosted OAuth redirects. When a sign-in started with /api/oauth/:provider/authorize fails after the user has left your site, Voult sends the browser back to your redirectUri with ?error=<CODE>&error_description=…&state=… instead of JSON. The codes are the ones below. @voult/express renames the parameter to voult_error on its way to your page.

Rate limits answer 429 with { "error": "<message>" } and no code yet. Treat any 429 as "slow down and retry later".

Rules

Client credentials and app

Code Status Meaning Fix
CLIENT_ID_REQUIRED 401 No X-Client-Id header. Send your app's client ID on every request. @voult/sdk does this from clientId.
CLIENT_SECRET_REQUIRED 401 The route needs X-Client-Secret and none was sent. Call this route from your server with the client secret. Never ship the secret to a browser.
INVALID_CLIENT 401 The client ID doesn't match an active app. Copy the client ID from the dashboard again; check the app isn't disabled.
INVALID_CLIENT_SECRET 401 The client secret is wrong. Copy it from the dashboard again. If you rotated it, update your server's env.
APP_NOT_FOUND 404 The app was deleted or deactivated. Check the app in the dashboard.
APP_NOT_ACTIVE 404 Hosted OAuth: the app is missing or inactive. Check the app in the dashboard.
IP_NOT_ALLOWLISTED 403 The app has an IP allowlist and the caller's IP isn't on it. Add your server's IP or CIDR in the dashboard, or remove the allowlist.
TOKEN_APP_MISMATCH 403 The end-user access token was issued for a different app than the client ID sent. Use tokens only with the app that issued them.
FETCH_PROVIDER_VISIBILITY_FAILED 500 Couldn't load which sign-in providers are visible (GET /api/provider-visibility/:clientId). Retry; report it if it persists.
CONFIG_ERROR 500 Voult itself is misconfigured. Not your bug. Retry later and report it if it persists.

Dashboard-only routes (audit logs, IP allowlist)

Code Status Meaning Fix
DEVELOPER_AUTH_REQUIRED 401 The route needs a signed-in Voult developer. Use the dashboard, or sign in to it first.
APP_ACCESS_DENIED 403 The signed-in developer doesn't own this app. Sign in with the account that owns the app.
INVALID_IP_OR_CIDR 400 The value isn't an IPv4 address or CIDR range. Use e.g. 203.0.113.7 or 203.0.113.0/24.
IP_ALLOWLIST_EXISTS 409 That IP or range is already on the allowlist. Nothing to do.
IP_ALLOWLIST_NOT_FOUND 404 No allowlist entry with that ID. Refresh the list; it may already be deleted.
IP_ALERT_NOT_FOUND 404 No IP alert with that ID. Refresh the list.

Sign-up and sign-in

Code Status Meaning Fix
VALIDATION_ERROR 400 A required field is missing or malformed. message names it. Fix the request body.
WEAK_PASSWORD 400 The password doesn't meet the rules (8+ characters, upper, lower, digit, one of @$!%*?&). Show PASSWORD_REQUIREMENTS_MESSAGE from @voult/sdk/validation and ask again.
INVALID_USERNAME 400 Username isn't 3–30 letters, digits or underscores. Ask for a different username.
USER_EXISTS 409 An account with this email already exists in this app. Offer sign-in or password reset instead.
USERNAME_TAKEN 409 The username is in use in this app. Ask for a different username.
INVALID_CREDENTIALS 401 Wrong email/username or password (also used when a password + MFA code pair is wrong). Ask the user to try again. Don't reveal which part was wrong.
ACCOUNT_LOCKED 423 Too many failed password or MFA attempts; the account is locked for 15 minutes. Tell the user to wait or reset their password.
ACCOUNT_DISABLED 401 / 403 The account was disabled by the user or the app. Show a "this account is disabled" message; POST /api/user/reenable (with the user's access token) if your app allows it.
EMAIL_NOT_VERIFIED 403 The email (or the provider's email) isn't verified. Ask the user to verify their email, or to verify it with the provider.
EMAIL_REQUIRED 400 / 403 The provider didn't share an email, and Voult needs one to create or match the account. Ask the user to make an email visible at the provider (e.g. a public or primary email on GitHub).
USER_NOT_FOUND 404 No account matches (e.g. a provider login for someone who never registered). Offer sign-up.
UNAUTHORIZED 401 The route needs an end-user access token and none (or an invalid or expired one) was sent. Refresh the session (refreshSession), then retry; sign in again if that fails.

Sessions and tokens

Access tokens live 15 minutes (ACCESS_TOKEN_TTL), refresh tokens 30 days. Every refresh returns a new refresh token; for 30 seconds the old one still returns that same new token, so parallel requests are safe.

Code Status Meaning Fix
INVALID_REFRESH_TOKEN 401 The refresh token is unknown, or was revoked by sign-out or session revocation. Sign the user in again.
REFRESH_TOKEN_EXPIRED 401 The refresh token is older than 30 days. Sign the user in again.
REFRESH_TOKEN_REUSE_DETECTED 401 A refresh token that was already exchanged was presented again more than 30 s later (REFRESH_REUSE_GRACE_SECONDS), which looks like theft, so Voult revoked all of the user's sessions. Parallel refreshes within the grace period get the same new token instead (since 1.3.0). Sign the user in again. If it happens without a cause, something is keeping and replaying old refresh tokens.
SESSION_NOT_FOUND 404 No session with that ID for this user. Refresh the session list.
INVALID_ID 400 An ID in the URL isn't a valid ID. Check the ID you passed.

MFA (TOTP)

Code Status Meaning Fix
MFA_ALREADY_ENABLED 400 MFA is already on. Nothing to do.
MFA_NOT_ENABLED 400 MFA is off, so there's nothing to disable or verify. Nothing to do.
MFA_SETUP_EXPIRED 400 The setup secret expired before it was confirmed. Call MFA setup again and scan the new QR code.
INVALID_MFA_TOKEN 400 / 401 The 6-digit or backup code is wrong. Ask for the code again.
INVALID_MFA_SESSION 401 The MFA-pending token from sign-in expired (5 minutes) or is invalid. Start sign-in again.

Passkeys (WebAuthn)

Code Status Meaning Fix
WEBAUTHN_CHALLENGE_EXPIRED 400 The registration or sign-in challenge expired. Request new options and try again.
WEBAUTHN_REGISTRATION_FAILED 400 The browser's registration response didn't verify. Try again; check the page's origin matches the app's configured origin.
WEBAUTHN_CREDENTIAL_EXISTS 409 This passkey is already registered. Nothing to do.
WEBAUTHN_NOT_REGISTERED 400 The account has no passkeys. Offer another sign-in method or passkey registration.
WEBAUTHN_CREDENTIAL_UNKNOWN 401 The passkey used isn't registered to any account in this app. Offer another sign-in method.
WEBAUTHN_AUTHENTICATION_FAILED 401 The passkey signature didn't verify. Try again.
WEBAUTHN_CREDENTIAL_NOT_FOUND 404 No passkey with that ID on this account. Refresh the passkey list.
Code Status Meaning Fix
INVALID_OR_EXPIRED_TOKEN 400 The magic-link token is wrong, used, or expired. Send a new link.
INVALID_VERIFICATION_LINK 400 The email-verification link is malformed. Send a new verification email.
TOKEN_EXPIRED 400 The email-verification link is used or expired. Send a new verification email.

Account

Code Status Meaning Fix
ACCOUNT_ALREADY_ACTIVE 400 Reactivating an account that isn't disabled. Nothing to do.
ACCOUNT_ALREADY_DISABLED 400 Disabling an account that's already disabled. Nothing to do.
NO_CHANGES_DETECTED 400 The profile update contains no changes. Nothing to do.

Hosted OAuth (/api/oauth/*)

These can arrive as JSON or as the error parameter on the redirect back to your redirectUri.

When the provider refuses (most often: the user pressed Cancel), its own lowercase OAuth 2.0 error is passed through unchanged, e.g. access_denied, with the provider's error_description. Those aren't Voult codes and aren't listed here; treat them as "sign-in didn't complete".

Code Status Meaning Fix
INVALID_PROVIDER 400 The provider name isn't supported. Use a provider the app supports; GET /api/apps/me lists them with their status.
PROVIDER_NOT_ENABLED 403 The provider is turned off for this app. Enable it in the dashboard.
PROVIDER_DISABLED_FOR_THIS_APP 403 Same as PROVIDER_NOT_ENABLED, raised at the callback. Enable it in the dashboard.
PROVIDER_NOT_CONFIGURED 400 The provider is enabled but its client ID or secret is missing. Add the provider credentials in the dashboard. npx voult doctor reports this.
INVALID_INTENT 400 intent isn't authenticate, login or register. Linking has its own route. Fix intent; use POST /api/oauth/:provider/link to link.
INVALID_STATE_PARAMETER 400 Your state isn't a string of at most 256 characters. Send a shorter string.
INVALID_CALLBACK_URL 400 The redirectUri isn't on the app's allowlist. For magic links (since 1.2.0): it isn't an allowlisted callback URL or on the same origin as one. Add the exact URL under Callback URLs in the dashboard (scheme, host, port and path must match). For magic links, any page on that callback URL's origin works.
INVALID_OAUTH_STATE 400 Voult's own state is missing, tampered with or older than 10 minutes. Start the sign-in again.
INVALID_LINK_STATE 400 A linking state without a user. Start linking again from a signed-in session.
INVALID_OAUTH_CALLBACK 400 The provider came back with neither an authorization code nor an error. Start the sign-in again.
PROVIDER_TOKEN_EXCHANGE_FAILED 400 The provider rejected the code exchange. message has the provider's reason. Check the provider credentials in the dashboard and the callback URL registered at the provider.
PROVIDER_PROFILE_FAILED 400 The provider issued a token but its profile endpoint failed. message has the provider's reason. Check the scopes/permissions of the OAuth app at the provider (e.g. a GitHub App needs "Email addresses" read access).
PROVIDER_ID_NOT_FOUND 400 The provider's profile had no user ID. Retry; report it if it persists.
PROVIDER_ALREADY_LINKED_TO_ANOTHER_USER 409 That provider account is already linked to a different user in this app. Sign in with that account, or unlink it there first.
MISSING_PARAMETERS 400 POST /api/oauth/exchange without code or redirectUri. Send both.
INVALID_OAUTH_CODE 400 The one-time voult_code is wrong, used, or expired. Start the sign-in again. Exchange the code once, right away.
OAUTH_CALLBACK_FAILED 500 Unexpected failure at the callback (redirect error only). Retry; report it if it persists.
INTERNAL_ERROR 500 Unexpected server error (any route). Retry; report it if it persists.

Provider token endpoints (/api/auth/<provider>/*)

Older routes where your app hands Voult a provider token or code directly. New integrations should use hosted OAuth.

Code Status Meaning Fix
GOOGLE_NOT_CONFIGURED 400 Google isn't enabled or configured for this app. Enable and configure Google in the dashboard.
GITHUB_NOT_CONFIGURED 400 GitHub isn't fully configured for this app. Add the GitHub credentials in the dashboard.
LINKEDIN_NOT_CONFIGURED 400 LinkedIn isn't fully configured for this app. Add the LinkedIn credentials in the dashboard.
LINKEDIN_NOT_ENABLED 400 LinkedIn is turned off for this app. Enable it in the dashboard.
FACEBOOK_NOT_ENABLED 400 Facebook is turned off for this app. Enable it in the dashboard.
MICROSOFT_NOT_ENABLED 400 Microsoft is turned off for this app. Enable it in the dashboard.
APPLE_NOT_ENABLED 400 Apple is turned off for this app. Enable it in the dashboard.
INVALID_GOOGLE_TOKEN 401 The Google ID or access token didn't verify. Get a fresh token from Google; check it was issued for the client ID in the dashboard.
INVALID_GITHUB_CODE 400 GitHub rejected the authorization code. Codes are single-use and short-lived: start again.
GITHUB_OAUTH_FAILED 400 GitHub returned an error. message has GitHub's reason. Check the GitHub app credentials and callback URL.
INVALID_FACEBOOK_TOKEN 401 The Facebook access token didn't verify. Get a fresh token from Facebook.
INVALID_MICROSOFT_ID_TOKEN 401 The Microsoft ID token didn't verify. Get a fresh token from Microsoft.
MICROSOFT_TOKEN_EXCHANGE_FAILED 401 Microsoft rejected the code exchange. Check the Microsoft credentials and redirect URI.
LINKEDIN_TOKEN_EXCHANGE_FAILED 401 LinkedIn rejected the code exchange. Check the LinkedIn credentials and redirect URI.
LINKEDIN_PROFILE_FETCH_FAILED 401 Couldn't read the LinkedIn profile with the token. Retry; check the LinkedIn app's scopes.
INVALID_APPLE_ID_TOKEN 401 The Apple ID token didn't verify. Get a fresh token from Apple.
APPLE_TOKEN_EXCHANGE_FAILED 401 Apple rejected the code exchange. Check the Apple credentials.