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

# Errors

> ApiError — the one exception the client throws, read straight off the server's envelope.

Every non-2xx response throws `ApiError`. There is exactly one shape, and it is a transcription of the server's [error envelope](/docs/api/errors) — the client adds no taxonomy of its own, so the two can never disagree.

## The `ApiError` class

```ts theme={"system"}
import { ApiError } from "@usenaive-sdk/vetta";
```

<ResponseField name="status" type="number">The HTTP status, e.g. `404`.</ResponseField>
<ResponseField name="type" type="string">The error family from the envelope, e.g. `not_found`, `invalid_request`.</ResponseField>
<ResponseField name="code" type="string">The specific machine code, e.g. `validation_failed`, `subscription_required`, `insufficient_credits`.</ResponseField>
<ResponseField name="param" type="string | undefined">The offending field, when the server names one. This is what the CLI prints.</ResponseField>
<ResponseField name="requestId" type="string">The server's correlation id for this call — quote it in a bug report; it is the single most useful field.</ResponseField>
<ResponseField name="message" type="string">The human-readable message from the envelope.</ResponseField>

## Catching

```ts theme={"system"}
import { ApiError } from "@usenaive-sdk/vetta";

try {
  await client.skills.get("skl_nope");
} catch (error) {
  if (error instanceof ApiError) {
    // error.status     404
    // error.type       "not_found"
    // error.code       "not_found"
    // error.param      the offending field, when the server names one
    // error.requestId  quote this in a bug report
  }
}
```

## What the client does not do

* **No retries.** A caller that wants retries owns that decision — the CLI and the dashboard want different policies. A `429` or `5xx` is thrown like any other error.
* **No code → status table.** `type` and `status` are read off the response. The server owns that mapping; a second copy here would be a second taxonomy that could drift.

## Errors worth planning for

| Code                    | Status | When                                                                                          |
| ----------------------- | ------ | --------------------------------------------------------------------------------------------- |
| `validation_failed`     | 400    | A body field is wrong; `param` names it.                                                      |
| `unauthorized`          | 401    | The key is missing, revoked, or the token expired.                                            |
| `insufficient_scope`    | 403    | The key lacks the scope the route requires.                                                   |
| `not_found`             | 404    | Unknown id — or a real id in the other mode (`test` vs `live`).                               |
| `conflict`              | 409    | e.g. `expected_version` on an agent update did not match.                                     |
| `subscription_required` | 402    | The organization has no active plan. See [`credits.subscription`](/docs/sdk/credits#subscription). |
| `insufficient_credits`  | 402    | The balance cannot cover the call. Top up first.                                              |

The full catalogue lives in the [API error reference](/docs/api/errors).
