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

# Common Errors

> Resolve common AIR Kit errors — HTTP status codes, Partner JWT signing failures, JWKS retrieval problems, and authentication-related integration issues.

## API error codes

These errors apply to the AIR Kit REST API (e.g. [Issuance API](/airkit/usage/credential/issuance-api)).

| HTTP status | Likely cause                                | How to fix                                                                                                                                                                                |
| ----------- | ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 400         | Missing or invalid request fields           | Verify `holderDid`, `schemaId`, `expiresAt`, `data`, `iv`, `authTag`, `encryptedKey`, and `externalId` are present and correctly typed, and that the credential data matches your schema. |
| 401         | Invalid or expired Partner JWT              | Verify the JWT is not expired (`exp`), the signature matches your JWKS key, `kid` in the header maps to the correct key, and `typ: "JWT"` is set.                                         |
| 403         | Feature not enabled or schema not allowed   | Confirm credential issuance is enabled for your partner account in the Developer Dashboard. Check that your `issuerDid` owns the schema.                                                  |
| 404         | Unknown issuer DID or program ID            | Double-check `issuerDid` and `credentialId` values against the Developer Dashboard.                                                                                                       |
| 409         | User consent rejected or duplicate conflict | The user has denied consent for credentials issued on their behalf, or a duplicate credential already exists for this user + schema. Dedupe before reissuing.                             |
| 500         | Server-side failure                         | Retry with exponential backoff. If persistent, check the [Moca Network status page](https://discord.gg/mocaversenft) or contact support.                                                  |

## SDK error names

Errors thrown by the SDK are `AirError` instances carrying a stable, machine-readable `name`. Branch on `name` rather than matching on the message, which is not part of the API contract.

```js theme={null}
try {
  await airService.login();
} catch (err) {
  if (err.name === "USER_CANCELLED") return; // expected, not a failure
  console.error(err.name, err.message);
}
```

**Client and user errors**

| Name                       | Meaning                                                          | How to handle                                                             |
| -------------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------- |
| `USER_CANCELLED`           | The user closed or dismissed an AIR Kit flow                     | Expected control flow — return quietly rather than surfacing an error     |
| `USER_REJECTED`            | The user declined a specific request, such as signing or consent | Leave the user where they were and let them retry                         |
| `CONFIG_ERROR`             | Invalid AIR Kit configuration                                    | Check your `partnerId`, `BUILD_ENV`, and `init()` options                 |
| `PERMISSION_NOT_ENABLED`   | The feature is not enabled for your partner account              | Enable it in the Developer Dashboard, or contact support                  |
| `ACCOUNT_DELETION_PENDING` | The account has a pending deletion request                       | Prompt the user to cancel deletion before continuing                      |
| `WINDOW_BLOCKED`           | The browser blocked a popup AIR Kit needed                       | Trigger the call directly from a user gesture so the popup is not blocked |
| `WINDOW_CLOSED`            | The user closed the AIR Kit popup before it finished             | Treat like a cancellation                                                 |

**Authentication and token errors**

| Name                                               | Meaning                                                       | How to handle                                                                                  |
| -------------------------------------------------- | ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `PARTNER_TOKEN_EXPIRED`                            | The Partner JWT you passed is past its `exp`                  | Mint a fresh JWT and retry. See [Partner authentication](/airkit/usage/partner-authentication) |
| `PARTNER_ACCESS_TOKEN_INVALID`                     | The Partner JWT failed validation                             | Check signing key, `kid`, and payload — see [JWT and JWKS errors](#jwt-and-jwks-errors) below  |
| `USER_MISMATCH`                                    | The token identifies a different user than the active session | Log out before switching users                                                                 |
| `TOKEN_EXPIRED` / `INVALID_TOKEN` / `UNAUTHORIZED` | The AIR session token is expired or rejected                  | Re-run `login()`                                                                               |

**Passwordless and OTP errors**

| Name                                                       | Meaning                                | How to handle                      |
| ---------------------------------------------------------- | -------------------------------------- | ---------------------------------- |
| `PASSWORDLESS_INVALID_CODE`                                | Wrong one-time password entered        | Let the user retype it             |
| `PASSWORDLESS_CODE_EXPIRED`                                | The one-time password expired          | Request a new code                 |
| `PASSWORDLESS_MAX_ATTEMPTS`                                | Too many incorrect attempts            | Ask the user to request a new code |
| `PASSWORDLESS_HOURLY_LIMIT` / `PASSWORDLESS_LOCK_EXCEEDED` | Rate limit reached for this identifier | Ask the user to try again later    |

**Linking errors**

| Name                               | Meaning                                       | How to handle              |
| ---------------------------------- | --------------------------------------------- | -------------------------- |
| `LINK_WALLET_ALREADY_LINKED`       | The wallet is already linked to this account  | No action needed           |
| `LINK_WALLET_LINKED_OTHER_ACCOUNT` | The wallet belongs to a different AIR account | Ask for a different wallet |
| `LINK_EMAIL_LINKED_OTHER_ACCOUNT`  | The email belongs to a different AIR account  | Ask for a different email  |

## JWT and JWKS errors

### "Invalid signature" or "JWT verification failed"

* Confirm the `kid` in your JWT header matches a key ID in your JWKS endpoint.
* Verify the algorithm (`RS256` or `ES256`) matches the key type in your JWKS.
* Check that you are signing with the correct private key.

### "Token expired"

* The `exp` claim has passed. Generate a new JWT with a fresh `exp` (recommended: 5 minutes from now).
* Verify your server clock is synchronized (NTP).

### "JWKS endpoint unreachable"

* Your JWKS URL must be publicly accessible over HTTPS — AIR servers cannot reach `localhost`. Use an HTTPS tunnel (ngrok, cloudflared) or deploy.
* Test the **exact URL registered in the Developer Dashboard**, not a guess. For example:

```bash theme={null}
curl https://your-domain/api/.well-known/jwks
```

* `air-examples` ships `/api/.well-known/jwks`; the plug-and-play template ships `/jwks.json`. Register whichever your app actually serves.
* Ensure no firewall or IP allowlist blocks AIR Kit's servers.
* Full setup procedure: [JWKS endpoint setup](/airkit/usage/jwks-setup).

### "kid not found"

* The `kid` in your JWT header does not match any key in the JWKS response.
* If you recently rotated keys, publish the new key to your JWKS endpoint before using it in JWTs.

### "Missing typ header"

* For server-side issuance, the JWT header must include `typ: "JWT"`. Add it to your JWT signing options:

```js theme={null}
jwt.sign(payload, privateKey, {
  algorithm: "RS256",
  header: { kid: "your-key-id", typ: "JWT" },
});
```

## CORS errors

If you see CORS errors when initializing the SDK in a browser:

* AIR Kit uses an iframe for login. Ensure your `Content-Security-Policy` allows `frame-src` from `*.air3.com`.
* If you are proxying requests, ensure the `Origin` header is forwarded correctly.

## Rate limiting

API endpoints may return `429 Too Many Requests` if you exceed rate limits. Back off and retry with exponential delays. Avoid tight polling loops on the status endpoint — start with 1-second intervals and double each attempt.
