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

# Error handling

> Exception types raised by the ReyGrid Python SDK and how to handle them.

All SDK exceptions inherit from `ReyGridError`.

```text theme={"system"}
ReyGridError
├── ReyGridApiError            # any 4xx / 5xx
│   ├── ReyGridAuthError       # 401
│   ├── ReyGridValidationError # 400
│   ├── ReyGridNotFoundError   # 404
│   ├── ReyGridConflictError   # 409
│   ├── ReyGridPermissionError # 403 (plan limit, model not on plan, restricted key)
│   └── ReyGridRateLimitError  # 429 (rate limited, key quota used up)
├── ReyGridNetworkError        # timeout, DNS, connection refused
└── ReyGridStreamError         # malformed SSE frame
```

Other 4xx/5xx statuses (for example 500) raise the base `ReyGridApiError`.

## Attributes

| Exception | Attribute | Description |
| - | - | - |
| `ReyGridApiError` | `status_code` | HTTP status |
| | `code` | Error code, or `"UNKNOWN"` |
| | `details` | The API's `error` object (dict): `code`, `message` and, for `INVALID_BODY`, `list` |
| `ReyGridError` (all) | `cause` | Underlying exception, when there is one |

## Handling

```python theme={"system"}
from reygrid import (
    ReyGridApiError,
    ReyGridAuthError,
    ReyGridNetworkError,
    ReyGridNotFoundError,
)

try:
    client.list_messages("{AgentId}", "user-42")
except ReyGridAuthError:
    print("Bad API key")
except ReyGridNotFoundError:
    print("No such conversation")
except ReyGridNetworkError as e:
    print(f"Network problem: {e}")
except ReyGridApiError as e:
    print(f"API {e.status_code}: {e.code} — {e}")
```

Catch subclasses before `ReyGridApiError`.

## Reading error details

`code` and the message come from the API's `error` object, so you can branch on the [error code](/rest-api/errors#error-codes) directly:

```python theme={"system"}
from reygrid import ReyGridApiError, ReyGridPermissionError

try:
    client.add_text_source("{AgentId}", "Refunds", "…")
except ReyGridPermissionError as e:
    if e.code == "PLAN_LIMIT_REACHED":
        print("Upgrade to add more knowledge")
except ReyGridApiError as e:
    if e.code == "INVALID_BODY":
        print((e.details or {}).get("list"))  # validation errors per field
    else:
        print(e.status_code, e.code, e)
```

<Info>
  Before 1.0.3, `code` was only read from the top level of the body, so most errors surfaced as `"UNKNOWN"`. Upgrade to get the real values.
</Info>

## Retries

The SDK doesn't retry. Retry `ReyGridNetworkError` and 5xx `ReyGridApiError` in your own code, with backoff. Don't retry 4xx errors without fixing the request.


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