Error codes
How Authio reports errors on protocol endpoints and on the admin API, the HTTP status behind each kind of failure, and the codes you are most likely to meet.
Authio answers errors in two shapes. Protocol endpoints speak OAuth 2.0, so standard libraries understand them; the admin API wraps every error in its envelope, with a stable errorCode at the top. Both carry the same codes underneath, so a failure reported by an integration can be matched to Authio’s logs.
Errors on protocol endpoints
The endpoints under /protocol/openid-connect/ answer with the fields of RFC 6749 §5.2 — error and error_description — plus error_code, the code behind the error:
{
"error": "invalid_grant",
"error_description": "Authorization code expired!",
"error_code": "AuthorizationCodeExpired"
}error | HTTP | Codes behind it |
|---|---|---|
invalid_client | 401 | InvalidClientCredentials: a wrong secret, or a secret sent with a method the client is not registered for. |
invalid_grant | 400 | AuthorizationCodeNotFound, AuthorizationCodeExpired, AuthorizationCodeClientMismatch, MissingCodeChallenge, MissingCodeVerifier, InvalidCodeVerifier, TokenExpired, RefreshTokenFailed, InvalidUserAuthentication, UserLockedOut, EmailNotConfirmed, UserSuspended |
invalid_request | 400 | InvalidRequest, InvalidRedirectUri, MissingGrantType, MissingCodeChallengeMethod, PkceMethodNotAllowed, RequiredFields, and a client that does not exist or is inactive. |
invalid_scope | 400 | InvalidScope: a scope the client has not enabled. |
invalid_target | 400 | InvalidTarget: a resource the client may not reach. |
unauthorized_client | 400 | GrantTypeDisabled; AccountNotEntitled when the subscription behind the application is not active. |
unsupported_grant_type | 400 | UnsupportedGrantType |
unsupported_response_type | 400 | UnsupportedResponseType |
access_denied | 400 | A denied device request; McpIntegrationNotIncluded, ConnectedAccountsNotIncluded or IntegrationLicenseSuspended when the plan does not cover the request. |
authorization_pending, slow_down, expired_token | 400 | Device flow polling: keep waiting, poll more slowly, or start again. |
temporarily_unavailable | 429 or 5xx | Rate limit reached, or a service Authio depends on is unavailable. Retry later. |
server_error | 500 | Something failed on Authio’s side. |
Introspection and revocation never fail because of the token itself: an invalid, expired or unknown token gets 200 with active: false from introspection, and a plain 200 from revocation. UserInfo answers 401 with invalid_token, or 403 with insufficient_scope, each with a WWW-Authenticate header.
During a sign-in in the browser, errors are not JSON. Until the redirect_uri is proven, they are shown on the client’s error page; after that, they go back to the redirect_uri with error, error_description, state and iss.
Errors on the admin API
The admin API answers every failure with its envelope: success is false, the HTTP status says what kind of failure it was, errorCode names it, and notifications explains it to a person. A validation error names each refused field:
{
"statusCode": 400,
"success": false,
"notifications": [
{
"message": "The description is too long.",
"field": "description",
"code": "ValidationFailed"
}
],
"errorCode": "ValidationFailed",
"requestId": "0HN8R3K5QJ2VA:00000004"
}Codes are names, such as NotFound or AlreadyExists, and the same name can come from different areas: a missing realm and a missing client both answer NotFound. Read the code together with the endpoint you called, and keep the requestId when you report a problem. The envelope itself is described in Admin API.
HTTP status by kind of failure
Every error Authio raises declares what kind of failure it is, and each kind answers with one status:
| Kind | HTTP | Meaning |
|---|---|---|
Invalid | 400 | The request carried something the rules refuse. |
Unauthorized | 401 | The caller has not proven who they are, or the proof did not hold. |
PaymentRequired | 402 | The action needs a plan or a payment the account does not have. |
Forbidden | 403 | The caller is known but may not do this. |
NotFound | 404 | What the request names does not exist, or is not visible to the caller. |
Conflict | 409 | The request clashes with the current state of what it names. |
Gone | 410 | What the request names existed once and is no longer available. |
Locked | 423 | What the request names is locked against the action. |
NotImplemented | 501 | The action is not supported here. |
UpstreamFailure | 502 | A service Authio depends on answered with a failure. |
Unavailable | 503 | The capability is temporarily unavailable. |
UpstreamTimeout | 504 | A service Authio depends on did not answer in time. |
| Anything unexpected | 500 | A failure the caller could not have caused. |
Requests refused before they reach an endpoint use the same envelope: 404 for an unknown route (NotFound), 405 for a method the route does not accept (MethodNotAllowed), 415 for a body that is not JSON (UnsupportedMediaType) and 429 for a rate limit (TooManyRequests).
Common codes
Requests and validation
| Code | HTTP | What it means | What to do |
|---|---|---|---|
ValidationFailed | 400 | The body broke a rule, or could not be read. | Fix the fields named in notifications. |
RequiredFields | 400 | Required fields are missing. | Send every field the endpoint requires. |
NotFound | 404 | The route or the resource does not exist, or belongs to another account. | Check the path and the IDs. |
Conflict | 409 | The change clashes with data saved meanwhile. | Read the resource again and retry. |
TooManyRequests | 429 | The rate limit was reached. | Wait the seconds in Retry-After. |
TemporarilyUnavailable | 503 | Authio could not reach its database or cache for a moment; nothing was changed. | Wait the seconds in Retry-After and send the same request again. |
UpstreamFailure, RequestTimeout | 502, 503, 504 | A service Authio depends on failed or did not answer. | Retry later. |
UnexpectedError | 500 | Something failed on Authio’s side. | Retry; if it persists, contact support with the requestId. |
Users and sign-in
| Code | HTTP | What it means | What to do |
|---|---|---|---|
Unauthorized | 401 | The call carries no valid credentials. | Send an API secret or a bearer token from the platform realm. |
Forbidden | 403 | The caller is authenticated but lacks the permission. | Grant the role, or allow it on the API secret. |
InvalidCredentials | 401 | The user name or the password is wrong. | Check them and try again. |
LockedOut | 401 | Too many failed attempts: the account is locked for a while. | Wait until the lockout ends. |
PasswordExpired | 401 | The password has to be renewed. | Renew it through the link sent by e-mail. |
AccountNotConfirmed | 401 | The address the account was created with is not confirmed. | Confirm it, then sign in again. |
AccountInactive | 401 | The account was deactivated. | An administrator of the realm activates it again. |
ClientAccessDenied | 401 | The credentials are right, but the account has no access to this application. | Give the account access to the client. |
UserSuspended | 401 | The user is suspended. | Lift or wait out the suspension. |
SubscriptionLapsed | 401 | The account has no active plan, so sign-in is paused. | Choose a plan to bring it back. |
FirstPurchasePending | 401 | The account was created on a paid plan that was never paid. | Finish the payment. |
DuplicateUserName, DuplicateEmail | 409 | The user name or e-mail is already taken. | Use another one. |
Realms and clients
| Code | HTTP | What it means | What to do |
|---|---|---|---|
NotFound | 404 | The realm or client does not exist, or belongs to another account. | Check the realm ID and the client ID. |
AlreadyExists | 409 | A realm or client with that ID already exists. | Pick another ID, or read the existing one. |
Inactive | 403 | The realm or client is deactivated. | Activate it again. |
Reserved | 409 | The ID is reserved: authio, system, platform, admin, master and internal cannot be used. | Pick another ID. |
SystemProtected | 403 | The platform’s own realm and client cannot be taken out of service. | Leave them active. |
InconsistentConfiguration | 400 | The client configuration contradicts itself and would prevent sign-in. | Fix the settings named in notifications. |
PublicClientHasNoSecret | 400 | A public client has no secret to rotate. | Make the client confidential first, if it needs a secret. |
Tokens and authorization
| Code | HTTP | What it means | What to do |
|---|---|---|---|
InvalidClientCredentials | 401 | The client secret is wrong, or sent with a method the client is not registered for. | Check the secret and the authentication method on the client. |
GrantTypeDisabled | 400 | The grant is not enabled on the client. | Enable it on the client. |
InvalidRedirectUri | 400 | The redirect_uri does not equal a registered one. | Register the exact address: scheme, host, port, path and query. |
InvalidScope | 400 | A requested scope is not enabled on the client. | Enable it, or stop asking for it. |
MissingCodeChallenge | 400 | The client requires PKCE and the request carried no code_challenge. | Send code_challenge and code_challenge_method. |
PkceMethodNotAllowed | 400 | The code_challenge_method is not enabled on the client. | Use S256, or enable the method. |
InvalidCodeVerifier | 400 | The code_verifier does not match the challenge. | Send the verifier the challenge was made from. |
AuthorizationCodeExpired | 400 | The code is valid for 30 seconds and was exchanged too late. | Exchange the code as soon as it arrives. |
UnsupportedGrantType | 400 | The grant_type is not one Authio issues. | Use one of the supported grants. |
InvalidTarget | 400 | The resource is not a resource server the client may reach. | Give the client access to the resource server. |
Plan and billing
| Code | HTTP | What it means | What to do |
|---|---|---|---|
RealmLimitExceeded, ClientLimitExceeded, UserLimitExceeded, ConsumerLimitExceeded, SecretLimitExceeded, ResourceServerLimitExceeded | 400 | The plan’s quota for that resource is used up. | Remove what you no longer use, or move to a larger plan. |
SeatLimitExceeded | 402 | Every seat of the team is taken. | Free a seat or add seats. |
NoActiveSubscription | 400 | The account has no active subscription. | Subscribe to a plan to create realms or clients. |
AccountNotEntitled | 403 | The subscription behind the application is not active, so sign-in and token issuance are paused. | Renew the subscription. |
McpIntegrationNotIncluded, ConnectedAccountsNotIncluded | 403 | The plan does not include MCP tokens or connected accounts. | Move to a plan that includes them. |
AuthLayoutNotIncluded | 403 | Customizing the login screens is not included in the plan. | Move to a plan that includes it. |
SharedTenancyNotIncluded | 403 | The account is on the Dedicated plan, so realms and clients are not created on the shared platform. | Create them in your environment’s own console. |
AI advisor
| Code | HTTP | What it means | What to do |
|---|---|---|---|
AiAdvisorNotIncludedInPlan | 403 | The AI advisor is not included in the plan. | Move to a plan that includes it. |
AiAdvisorNotEnabled | 409 | The advisor is not turned on for this realm or client. | Turn it on for that realm or client. |
AiAdvisorRequiresAccountOperator | 403 | Only a person signed in to the console can use the advisor; machine credentials such as API keys cannot. | Call it from a signed-in session. |
AiAdvisorNotConfigured | 503 | The advisor is not available on this deployment. | On a Dedicated environment, set ANTHROPIC_API_KEY in Configuration. |
AiAnalysisAlreadyRunning, AiConfigurationAlreadyRunning, AiAccessReviewAlreadyRunning, AiDashboardSummaryAlreadyRunning | 409 | The same reading is already being produced. | Wait for it to finish before asking again. |
AiConfigurationNotActionable | 409 | The proposal is not in a state that allows this: it carries no changes, was already applied, or was never applied. | Reload the proposal. |
AiConfigurationStale | 409 | The client changed after the proposal was made, so applying it would undo that change. | Ask for a new proposal. |
AiAccessReviewPeriodInvalid | 400 | The period must last at least one hour and at most 90 days, and end no later than now. | Pick a period within those bounds. |
AiTokenShapeCarriesToken | 400 | The token shape sent for analysis carries the token itself. | Send the token’s shape without the token. |
AiRunLimitExceeded | 400 | The plan’s AI runs for the period are used up. | Wait until the count starts again, or move to a larger plan. |
Dedicated environment
| Code | HTTP | What it means | What to do |
|---|---|---|---|
DedicatedInstanceNotFound | 404 | The account has no environment yet. | It is prepared as soon as the subscription is confirmed. |
DedicatedInstanceNotReady | 409 | The environment is not running, so it cannot be changed. | Try again once it is ready. |
DedicatedSubscriptionMissing | 409 | No live Dedicated subscription covers the account. | Subscribe to the Dedicated plan. |
DedicatedVariableNotEditable | 400 | The variable is managed by the platform. | Change only the variables listed in Configuration. |
DedicatedAdminCredentialUnavailable | 409 | The first administrator’s password was already shown and is no longer kept. | Use password recovery on your environment’s sign-in page. |
DedicatedDomainInvalid | 400 | The host names cannot be used: invalid, equal, not under the same domain, or under the platform’s domain. | Use two host names of yours under one domain. |
DedicatedDomainAlreadySet | 409 | The environment already has a domain of your own. | Remove it before adding another. |
DedicatedDomainNotReady | 409 | The domain’s certificates are not issued yet. | Check the DNS records and try again in a few minutes. |
DedicatedServiceNotRunning | 409 | The service has no running deployment. | Redeploy it to start it. |
DedicatedDeploymentNotFound | 404 | The deployment is not one of the environment’s recent deployments. | Reload the services and pick it again. |
DedicatedDeploymentNotCancellable | 409 | Only a deployment still being built or rolled out can be cancelled. | Nothing to cancel; redeploy if needed. |
DedicatedInstanceRemovalFailed | 502 | The hosting provider did not finish removing the environment. | Remove it again; it picks up where it stopped. |