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

# Errors and retries

> Read AIRCTRL errors, decide when to retry, and keep requests safe from duplicates.

export function AirctrlWordmark() {
  return <span className="airctrl-wordmark" aria-label="AIRCTRL">
      <span aria-hidden="true" className="airctrl-wordmark-air">AIR</span>
      <span aria-hidden="true" className="airctrl-wordmark-ctrl">CTRL</span>
    </span>;
}

<AirctrlWordmark /> uses one JSON shape for API errors:

For the failures relevant to each exact endpoint, use the [API error matrix](/reference/api-errors).
For request context, pagination, retries and safe updates, use [Using the API](/reference/api-usage).

```json theme={}
{
  "ok": false,
  "error": {
    "code": "forbidden",
    "message": "not_authorized"
  },
  "requestId": "req_example"
}
```

`error.code` is the stable HTTP category, such as `bad_request`, `unauthorized` or `forbidden`.
`error.message` contains the specific <AirctrlWordmark /> reason, such as `not_authorized`. `error.details` may
contain validation details. Keep `requestId` when contacting support.

## HTTP status guide

| Status | Meaning                                            | What to do                                                                                                |
| ------ | -------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `400`  | The request is invalid                             | Fix the submitted fields, identifiers or query values.                                                    |
| `401`  | Authentication failed                              | Supply a valid, unexpired PAT or Service Account token.                                                   |
| `403`  | Permission or resource access was denied           | Check the caller's role and project access. Do not retry unchanged.                                       |
| `404`  | The resource is unavailable                        | Check the ID and account context. Some read operations return `null` to avoid revealing hidden resources. |
| `409`  | The resource state conflicts with the request      | Refresh state. For idempotency conflicts, do not reuse the key with different input.                      |
| `422`  | The input is understood but cannot be processed    | Correct the business-level input before retrying.                                                         |
| `429`  | The request limit was exceeded                     | Wait and retry with backoff.                                                                              |
| `500`  | <AirctrlWordmark /> could not complete the request | Retry later. Keep the request ID if the error continues.                                                  |
| `503`  | A required service is temporarily unavailable      | Retry later with backoff.                                                                                 |

## SDK errors

The SDK throws `AirctrlError` for API failures.

```ts theme={}
import { AirctrlError, createClient } from '@airctrl/sdk'

const airctrl = createClient({
  baseUrl: process.env.AIRCTRL_API_URL!,
  auth: process.env.AIRCTRL_TOKEN!,
})

try {
  await airctrl.getProject(
    '00000000-0000-4000-8000-000000000010',
    { accountId: '00000000-0000-4000-8000-000000000001' },
  )
} catch (error) {
  if (error instanceof AirctrlError) {
    console.error(error.status, error.code, error.message)
  }
}
```

## CLI errors

The CLI prints stable error codes instead of raw server or filesystem messages. A non-zero exit status means the command did not complete.

```bash theme={}
if ! airctrl project list --account "$AIRCTRL_ACCOUNT_ID"; then
  echo "AIRCTRL command failed" >&2
  exit 1
fi
```

Do not parse human-facing prose from terminal output. Use the command exit status and the stable code written to stderr.

## MCP errors

<AirctrlWordmark /> tools return an MCP error result. Your agent can read the safe code and explanation, but it must not keep retrying permission, validation, or missing-resource errors without changing the request.

## Authentication and access reasons

The values in this section appear in `error.message`. The SDK exposes the HTTP category through
`AirctrlError.code` and the specific reason through `AirctrlError.message`.

| Code                            | Meaning                                                              | What to do                                                      |
| ------------------------------- | -------------------------------------------------------------------- | --------------------------------------------------------------- |
| `missing_bearer_token`          | The request has no bearer token.                                     | Send `Authorization: Bearer <token>`.                           |
| `invalid_authorization_header`  | The authorization header has the wrong shape.                        | Use one bearer token and no extra text.                         |
| `unsupported_token`             | The token prefix or identity type is not supported.                  | Use a current <AirctrlWordmark /> PAT or Service Account token. |
| `invalid_pat`                   | The PAT could not be verified.                                       | Create a new PAT and replace the configured value.              |
| `pat_revoked`                   | The PAT was revoked.                                                 | Create and configure a replacement.                             |
| `pat_locked`                    | <AirctrlWordmark /> locked the PAT.                                  | Review the token in the web app and replace it if needed.       |
| `pat_expired`                   | The PAT passed its expiration time.                                  | Create a new PAT.                                               |
| `pat_needs_reissue`             | The PAT uses an older format that must be replaced.                  | Reissue it in Settings > Access tokens.                         |
| `service_account_revoked`       | The Service Account no longer exists for active use.                 | Use an active Service Account.                                  |
| `service_account_disabled`      | The Service Account is paused.                                       | Reactivate it in the web app after review.                      |
| `sa_credential_not_active`      | The selected Service Account credential is not active.               | Configure its current active SDK credential.                    |
| `sa_credential_required`        | Service Account authentication is missing required credential proof. | Configure the complete credential bundle.                       |
| `service_account_not_supported` | This public operation is human-only.                                 | Use a human PAT if the operation is approved.                   |
| `pat_read_only`                 | A read-only PAT tried to write.                                      | Use a read/write PAT or keep the operation read-only.           |
| `not_account_member`            | The human identity is not a member of the account.                   | Select an account the user belongs to.                          |
| `account_context_missing`       | The request did not identify the account.                            | Send `x-account-id` or the SDK/CLI account option.              |
| `not_authorized`                | The identity cannot access the selected resource.                    | Check membership, role and resource access.                     |
| `insufficient_permission`       | The identity lacks a required RBAC permission.                       | Assign the needed role in the web app.                          |

## Resource and validation reasons

| Code                             | Meaning                                                            | What to do                                                            |
| -------------------------------- | ------------------------------------------------------------------ | --------------------------------------------------------------------- |
| `project_not_found`              | The project is unavailable to the caller.                          | Recheck the project ID and account.                                   |
| `record_not_found`               | The record is unavailable to the caller.                           | Recheck the record ID and project access.                             |
| `record_name_exists`             | The project already has a record with that name.                   | Choose another name or update the existing record.                    |
| `target_not_a_member`            | A sharing target is not an eligible project or account member.     | Add and onboard the person through the web app first.                 |
| `recipient_public_key_not_found` | <AirctrlWordmark /> cannot wrap the record key for that recipient. | Ask the recipient to finish onboarding.                               |
| `group_not_found`                | The selected group is unavailable.                                 | Recheck the group ID.                                                 |
| `group_account_mismatch`         | The group belongs to another account.                              | Use a group from the record's account.                                |
| `group_project_mismatch`         | The group belongs to another project.                              | Use a group from the record's project.                                |
| `scope_id_required`              | A scope type was supplied without its ID.                          | Add the matching account or project ID.                               |
| `invalid_day`                    | The audit day is invalid.                                          | Use `YYYY-MM-DD`.                                                     |
| `project_not_keyed`              | The project is missing required encryption setup.                  | Open the project in the web app and complete its setup.               |
| `project_key_version_conflict`   | The submitted encrypted data used an outdated project key version. | Refresh project crypto metadata and repeat the local encryption step. |

## Gateway and provider reasons

| Code                             | Meaning                                                              | What to do                                           |
| -------------------------------- | -------------------------------------------------------------------- | ---------------------------------------------------- |
| `gateway_not_found`              | The gateway is unavailable to the caller.                            | Recheck the gateway ID and project access.           |
| `credential_not_found`           | The provider credential is unavailable.                              | Recheck the credential ID and account.               |
| `provider_not_found`             | The provider is unknown or disabled.                                 | Use a provider returned by `listProviders()`.        |
| `token_not_found`                | The gateway token is unavailable.                                    | Refresh the token list and recheck its ID.           |
| `log_not_found`                  | The requested usage log is unavailable.                              | Recheck the log ID and logging settings.             |
| `service_account_not_in_account` | A gateway principal belongs to another account.                      | Choose a Service Account from the gateway's account. |
| `service_account_inactive`       | The selected Service Account is paused or revoked.                   | Review it in the web app before reactivation.        |
| `principal_user_not_member`      | The selected user is not an eligible account member.                 | Add or restore account membership first.             |
| `base_url_not_allowed`           | A custom provider URL failed <AirctrlWordmark />'s URL safety rules. | Use an approved HTTPS endpoint.                      |
| `plan_limit_reached`             | The account plan does not allow another resource.                    | Review the plan or remove an unused resource.        |

## Request and service reasons

| Reason                                         | Meaning                                             | Retry?                                                |
| ---------------------------------------------- | --------------------------------------------------- | ----------------------------------------------------- |
| `idempotency_key_reused_with_different_params` | The same key was used for a different write.        | No. Create a new key for the new intended action.     |
| `idempotency_request_in_progress`              | An identical write is still processing.             | Yes, after a short delay, with the same key and body. |
| `rate_limited`                                 | The caller exceeded a request limit.                | Yes, with bounded exponential backoff.                |
| `internal_error`                               | <AirctrlWordmark /> could not complete the request. | Yes, a limited number of times. Keep `requestId`.     |
| `service_unavailable`                          | A required service is temporarily unavailable.      | Yes, with bounded exponential backoff.                |

## Validation example

```json theme={}
{
  "ok": false,
  "error": {
    "code": "bad_request",
    "message": "Bad Request Exception",
    "details": [
      "name must not be empty"
    ]
  },
  "requestId": "req_example"
}
```

Show validation details to a developer, but do not expose raw internal exceptions or secret values in logs.

## Safe retries

<AirctrlWordmark /> accepts `Idempotency-Key` on writes. Use one UUID for one intended action. A retry with the same key and the same body reuses the first result. Reusing the key with different input returns `409`.

The SDK creates an idempotency key for writes. For record creation, pass a stable `idempotencyKey` when your own job may restart and call the method again.

Retry only temporary failures such as `429`, `500`, and `503`. Use exponential backoff with jitter and a maximum attempt count.
