> ## Documentation Index
> Fetch the complete documentation index at: https://docs.busha.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> Every OAuth2 and API error, what causes it, and how to fix it.

## Error envelopes

Errors come in two shapes depending on which surface returned them.

**OAuth2 protocol endpoints** (`/oauth2/auth`, `/oauth2/token`, `/oauth2/revoke`) return RFC 6749 §5.2 shape:

```json theme={null}
{
  "error": "invalid_grant",
  "error_description": "code has already been used"
}
```

**Busha resource API** errors return:

```json theme={null}
{
  "status": "error",
  "message": "Insufficient scope",
  "error": {
    "name": "insufficient_scope",
    "message": "this endpoint requires balances:read"
  }
}
```

`error_description` is human-readable diagnostic text — do not surface it verbatim to end users.

## OAuth2 errors

| Error | Surface | Cause | Fix |
| - | - | - | - |
| `invalid_request` | All OAuth2 endpoints | Malformed request — missing parameter, bad encoding, wrong content type. | Validate your request shape against the [Endpoints reference](./endpoints). |
| `invalid_client` | `/oauth2/token` | Wrong `client_id`, wrong `client_secret`, or wrong authentication method. | Use HTTP Basic auth with URL-encoded `client_id:client_secret`. |
| `invalid_grant` | `/oauth2/token` | Code reused, code expired (10 min window), PKCE mismatch, or refresh token already rotated (family revoked). | For codes: make your callback handler idempotent — deduplicate on `state` or the code itself. For refresh tokens: treat as a hard disconnect and prompt re-authorization. |
| `invalid_scope` | `/oauth2/auth` | Scope not in the [catalog](./scopes), or not in your `approved_scopes`. | Compare your requested scopes against the catalog and your approved set on the dashboard. |
| `unauthorized_client` | `/oauth2/auth` | Client status is `pending`, `rejected`, or `suspended`. | Check your app status on the dashboard. |
| `access_denied` | Callback (`/callback`) | User clicked Cancel on the consent screen. | Render a "continue without connecting" path in your UI. |
| `redirect_uri does not match` | OAuth2 endpoints | `redirect_uri` is not byte-identical to a registered URI. | Compare bytes against the dashboard value. Check trailing slash, scheme, port, path casing. |

## Resource API errors

| HTTP status | Error name | Cause | Fix |
| - | - | - | - |
| `401 Unauthorized` | — | Access token expired, revoked, or signature verification failed. | Refresh the access token. Force a JWKS refetch if the `kid` is unknown. |
| `403 Forbidden` | `insufficient_scope` | The token does not include the scope required by this endpoint. | Inspect the `scp` claim in the access token. Re-authorize with the broader scope set. |

## Common gotchas

**`invalid_grant` on code exchange**
Token exchange is not idempotent — authorization codes are single-use. If your callback handler runs twice (user double-clicks, browser retry), the second exchange returns `invalid_grant`. Deduplicate on the `state` value or the `code` itself in your callback handler.

**`invalid_grant` on refresh**
A refresh token can only be used once. If you replay a previously-used refresh token, Busha revokes the entire token family — all tokens for that user are immediately invalidated. Handle any `invalid_grant` on a refresh as a hard disconnect and send the user through the authorization flow again.

**Access token still works after refresh token revocation**
Revoking a refresh token prevents new tokens from being minted, but does not invalidate an already-issued access token. JWTs self-expire at `exp` (\~1 hour). This is by design. If you need instant invalidation, layer a server-side allowlist on critical endpoints.

**`openid` scope not shown on consent screen**
`openid` and `offline_access` are protocol scopes that Busha hides from the consent UI. If you received an `id_token` and `refresh_token`, they were granted successfully.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.