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

# Errors

> Every Arcus API error uses a consistent JSON envelope. Here is the full taxonomy.

## Error envelope

All errors return a JSON body in this shape:

```json theme={null}
{
  "error": "not_found",
  "code": "not_found",
  "type": "not_found",
  "hint": "The requested order does not exist or does not belong to this entity.",
  "param": null,
  "request_id": "req_01H..."
}
```

| Field        | Always present | Description                                              |
| ------------ | -------------- | -------------------------------------------------------- |
| `error`      | Yes            | Machine-readable error key                               |
| `code`       | Yes            | Same as `error` (included for forward compatibility)     |
| `type`       | No             | Error category (see below)                               |
| `hint`       | No             | Human-readable one-sentence explanation                  |
| `param`      | No             | The request parameter that caused the error              |
| `required`   | No             | The scope required (only on `insufficient_scope` errors) |
| `request_id` | No             | Unique request ID for support tracing                    |

## HTTP status codes

| Status                      | Meaning                                                                   |
| --------------------------- | ------------------------------------------------------------------------- |
| `200 OK`                    | Success                                                                   |
| `201 Created`               | Resource created                                                          |
| `204 No Content`            | Success with no body (DELETE)                                             |
| `304 Not Modified`          | ETag matched; use your cached copy                                        |
| `400 Bad Request`           | Invalid request body or parameters                                        |
| `401 Unauthorized`          | Missing or invalid API key                                                |
| `403 Forbidden`             | Valid key but insufficient scope or IP not allowlisted                    |
| `404 Not Found`             | Resource does not exist or belongs to a different entity                  |
| `409 Conflict`              | State conflict (idempotency key mismatch, duplicate record, RI violation) |
| `422 Unprocessable Entity`  | Validation error on request body                                          |
| `429 Too Many Requests`     | Rate limit exceeded                                                       |
| `500 Internal Server Error` | Arcus-side error; safe to retry with exponential backoff                  |

## Error types

| Type               | HTTP status | Description                                            |
| ------------------ | ----------- | ------------------------------------------------------ |
| `validation_error` | 400 / 422   | Request body or parameter failed validation            |
| `permission_error` | 401 / 403   | Auth or scope issue                                    |
| `not_found`        | 404         | Resource not found                                     |
| `conflict`         | 409         | State conflict                                         |
| `rate_limit`       | 429         | Too many requests                                      |
| `expand_error`     | 400         | Expand parameter syntax or limit violated              |
| `not_implemented`  | 501         | Endpoint exists but the operation is not yet available |
| `internal`         | 500         | Arcus-side error                                       |

## Common error codes

### Authentication and authorization

| Code                 | Status | Description                                          |
| -------------------- | ------ | ---------------------------------------------------- |
| `missing_api_key`    | 401    | `Authorization` header not provided                  |
| `invalid_api_key`    | 401    | Key not found or revoked                             |
| `missing_entity_id`  | 401    | Entity ID not present in the URL path                |
| `entity_id_mismatch` | 403    | Entity ID in the URL does not match the key's entity |
| `insufficient_scope` | 403    | Key lacks the required scope                         |
| `ip_not_allowlisted` | 403    | Request IP is not in the key's allowlist             |
| `key_expired`        | 401    | Key has passed its expiry date                       |

### Validation

| Code                       | Status | Description                                   |
| -------------------------- | ------ | --------------------------------------------- |
| `invalid_id_format`        | 400    | Path parameter is not a valid UUID            |
| `missing_required_field`   | 422    | Required field missing from request body      |
| `invalid_field_value`      | 422    | Field value fails type or range validation    |
| `idempotency_key_mismatch` | 409    | Same key reused with a different request body |

### Resource state

| Code              | Status | Description                                            |
| ----------------- | ------ | ------------------------------------------------------ |
| `not_found`       | 404    | Resource not found or belongs to a different entity    |
| `conflict`        | 409    | Operation conflicts with current resource state        |
| `resource_in_use` | 409    | Cannot delete; resource is referenced by other records |

### Expand

| Code                                       | Status | Description                                 |
| ------------------------------------------ | ------ | ------------------------------------------- |
| `invalid_expand_unknown_field`             | 400    | Expand path does not exist on this resource |
| `invalid_expand_too_many_paths`            | 400    | More than 10 expand paths in one request    |
| `invalid_expand_depth_exceeded`            | 400    | Expand path nesting deeper than 4 levels    |
| `invalid_expand_list_requires_data_prefix` | 400    | List endpoint expand missing `data.` prefix |

### Rate limits

| Code                  | Status | Description                                        |
| --------------------- | ------ | -------------------------------------------------- |
| `rate_limit_exceeded` | 429    | Per-key rate limit hit; check `Retry-After` header |

## Handling errors in code

<CodeGroup>
  ```javascript Node theme={null}
  import Arcus, { ArcusAPIError } from '@arcuserp/sdk-node';

  try {
    const order = await arcus.orders.retrieve('ord_invalid');
  } catch (err) {
    if (err instanceof ArcusAPIError) {
      console.error(err.code);      // e.g. "not_found"
      console.error(err.hint);      // human-readable
      console.error(err.requestId); // for support
      console.error(err.status);    // HTTP status code

      if (err.status === 429) {
        // Respect Retry-After
        const retryAfter = err.headers['retry-after'];
        await sleep(parseInt(retryAfter, 10) * 1000);
      }
    }
  }
  ```

  ```python Python theme={null}
  from arcuserp import ArcusAPIError

  try:
      order = arcus.orders.retrieve('ord_invalid')
  except ArcusAPIError as e:
      print(e.code)       # e.g. "not_found"
      print(e.hint)       # human-readable
      print(e.request_id) # for support
      print(e.status)     # HTTP status

      if e.status == 429:
          import time
          time.sleep(int(e.headers.get('retry-after', 1)))
  ```

  ```go Go theme={null}
  import arcus "github.com/arcuserp/sdk-go"

  order, err := client.Orders.Get(ctx, "ord_invalid", nil)
  if err != nil {
      if apiErr, ok := err.(*arcus.APIError); ok {
          fmt.Println(apiErr.Code)      // e.g. "not_found"
          fmt.Println(apiErr.Hint)      // human-readable
          fmt.Println(apiErr.RequestID) // for support
          fmt.Println(apiErr.StatusCode)
      }
  }
  ```
</CodeGroup>

## Retry strategy

* **5xx errors:** safe to retry with exponential backoff (1s, 2s, 4s, 8s, max 5 retries)
* **429 errors:** wait the number of seconds in the `Retry-After` response header before retrying
* **4xx errors (except 429):** do not retry; fix the request first
* **Always use `Idempotency-Key`** on retried POST/PATCH/DELETE requests to prevent duplicates

## Support

Include `request_id` when contacting support. It allows the Arcus team to retrieve the full trace for the failing request.
