# Errors

Error classes for handling different types of failures in the Python SDK.

The SDK defines specific error classes for different failure scenarios. All error classes extend the base `SurrealError` class, allowing you to catch and handle specific error types with `isinstance` checks.

**Source:** [errors.py](https://github.com/surrealdb/surrealdb.py/blob/main/src/surrealdb/errors.py)

## Base error

### `SurrealError` {#surrealerror}

Base class for every error raised by the SurrealDB Python SDK. Extends Python's built-in `Exception`.

```python
from surrealdb import SurrealError

try:
    result = db.select("users")
except SurrealError as e:
    print("SDK error:", e)
```

## Server errors

Server errors originate from the SurrealDB server and carry structured information about the failure. All server errors extend `ServerError`.

### `ServerError` {#servererror}

**Extends:** [`SurrealError`](#surrealerror)

Error received from the SurrealDB server with structured kind, details, and cause chain.

#### Properties

<table>
    <thead>
        <tr>
            <th>Property</th>
            <th>Type</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><code>kind</code></td>
            <td><code>str</code></td>
            <td>The structured error kind (e.g. <code>"NotAllowed"</code>, <code>"NotFound"</code>). Match against <a href="#errorkind"><code>ErrorKind</code></a> constants.</td>
        </tr>
        <tr>
            <td><code>code</code></td>
            <td><code>int</code></td>
            <td>Legacy JSON-RPC error code. <code>0</code> when unavailable.</td>
        </tr>
        <tr>
            <td><code>details</code></td>
            <td><code>dict[str, Any] | None</code></td>
            <td>Kind-specific structured details. <code>None</code> when not provided.</td>
        </tr>
        <tr>
            <td><code>server_cause</code></td>
            <td><code>ServerError | None</code></td>
            <td>The underlying server error in the cause chain, if any.</td>
        </tr>
    </tbody>
</table>

#### Methods

#### `.has_kind()` {#has-kind}

Check if this error or any cause in the chain matches the given kind.

```python
has_kind(kind: str) -> bool
```

```python
from surrealdb import ErrorKind

try:
    db.query("INVALID").execute()
except ServerError as e:
    if e.has_kind(ErrorKind.VALIDATION):
        print("Validation error")
```

---

#### `.find_cause()` {#find-cause}

Find the first error in the cause chain matching the given kind.

```python
find_cause(kind: str) -> ServerError | None
```

```python
try:
    db.signin({"username": "user", "password": "wrong"})
except ServerError as e:
    auth_cause = e.find_cause(ErrorKind.NOT_ALLOWED)
    if auth_cause:
        print("Auth failure:", auth_cause)
```

---

### `ValidationError` {#validationerror}

**Extends:** [`ServerError`](#servererror)

Validation failure such as parse errors, invalid request parameters, or bad input.

**Properties:**

| Property | Type | Description |
|----------|------|-------------|
| `is_parse_error` | `bool` | Whether this is a query parse error |
| `parameter_name` | `str \| None` | The invalid parameter name, if applicable |

---

### `ConfigurationError` {#configurationerror}

**Extends:** [`ServerError`](#servererror)

Feature or configuration not supported, such as live queries or GraphQL.

**Properties:**

| Property | Type | Description |
|----------|------|-------------|
| `is_live_query_not_supported` | `bool` | Whether live queries are not supported |

---

### `ThrownError` {#thrownerror}

**Extends:** [`ServerError`](#servererror)

User-thrown error via the `THROW` statement in SurrealQL.

---

### `QueryError` {#queryerror}

**Extends:** [`ServerError`](#servererror)

Query execution failure such as timeout, cancellation, or not executed.

**Properties:**

| Property | Type | Description |
|----------|------|-------------|
| `is_not_executed` | `bool` | Whether the query was not executed |
| `is_timed_out` | `bool` | Whether the query timed out |
| `is_cancelled` | `bool` | Whether the query was cancelled |
| `timeout` | `dict[str, Any] \| None` | The timeout duration as `{"secs": ..., "nanos": ...}` |

---

### `SerializationError` {#serializationerror}

**Extends:** [`ServerError`](#servererror)

Serialisation or deserialisation failure.

**Properties:**

| Property | Type | Description |
|----------|------|-------------|
| `is_deserialization` | `bool` | Whether this is a deserialisation error |

---

### `NotAllowedError` {#notallowederror}

**Extends:** [`ServerError`](#servererror)

Permission denied, method not allowed, or function/scripting blocked.

**Properties:**

| Property | Type | Description |
|----------|------|-------------|
| `is_token_expired` | `bool` | Whether the authentication token has expired |
| `is_invalid_auth` | `bool` | Whether the authentication credentials are invalid |
| `is_scripting_blocked` | `bool` | Whether scripting is blocked |
| `method_name` | `str \| None` | The disallowed method name |
| `function_name` | `str \| None` | The disallowed function name |
| `target_name` | `str \| None` | The disallowed target name |

```python
from surrealdb import NotAllowedError

try:
    db.signin({"username": "user", "password": "wrong"})
except NotAllowedError as e:
    if e.is_token_expired:
        print("Token expired, re-authenticate")
    elif e.is_invalid_auth:
        print("Invalid credentials")
```

---

### `NotFoundError` {#notfounderror}

**Extends:** [`ServerError`](#servererror)

Resource not found such as table, record, namespace, or method.

**Properties:**

| Property | Type | Description |
|----------|------|-------------|
| `table_name` | `str \| None` | The missing table name |
| `record_id` | `str \| None` | The missing record ID |
| `method_name` | `str \| None` | The missing method name |
| `namespace_name` | `str \| None` | The missing namespace name |
| `database_name` | `str \| None` | The missing database name |
| `session_id` | `str \| None` | The missing session ID |

---

### `AlreadyExistsError` {#alreadyexistserror}

**Extends:** [`ServerError`](#servererror)

Duplicate resource such as record, table, or namespace.

**Properties:**

| Property | Type | Description |
|----------|------|-------------|
| `record_id` | `str \| None` | The duplicate record ID |
| `table_name` | `str \| None` | The duplicate table name |
| `session_id` | `str \| None` | The duplicate session ID |
| `namespace_name` | `str \| None` | The duplicate namespace name |
| `database_name` | `str \| None` | The duplicate database name |

---

### `InternalError` {#internalerror}

**Extends:** [`ServerError`](#servererror)

Internal or unexpected server error. Used as a fallback when the error kind is not recognized.

## SDK-side Errors

These errors originate from the SDK itself, not the server.

### `ConnectionUnavailableError` {#connectionunavailableerror}

**Extends:** [`SurrealError`](#surrealerror)

Thrown when attempting an operation without an active connection.

```python
from surrealdb import Surreal, ConnectionUnavailableError

db = Surreal("ws://localhost:8000")

try:
    db.select("users")
except ConnectionUnavailableError:
    print("Not connected to database")
```

---

### `UnsupportedEngineError` {#unsupportedengineerror}

**Extends:** [`SurrealError`](#surrealerror)

Thrown when the URL protocol is not supported.

**Properties:**

| Property | Type | Description |
|----------|------|-------------|
| `url` | `str` | The unsupported URL |

```python
from surrealdb import Surreal, UnsupportedEngineError

try:
    db = Surreal("ftp://localhost:8000")
except UnsupportedEngineError as e:
    print(f"Unsupported protocol: {e.url}")
```

---

### `UnsupportedFeatureError` {#unsupportedfeatureerror}

**Extends:** [`SurrealError`](#surrealerror)

Thrown when a feature is not supported by the current connection type. For example, sessions and transactions require a WebSocket connection.

```python
from surrealdb import AsyncSurreal, UnsupportedFeatureError

db = AsyncSurreal("http://localhost:8000")
await db.connect()

try:
    session = await db.new_session()
except UnsupportedFeatureError:
    print("Sessions require a WebSocket connection")
```

---

### `UnexpectedResponseError` {#unexpectedresponseerror}

**Extends:** [`SurrealError`](#surrealerror)

Thrown when the server returns an unexpected response format.

---

### `InvalidRecordIdError` {#invalidrecordiderror}

**Extends:** [`SurrealError`](#surrealerror)

Thrown when a `RecordID` string could not be parsed.

---

### `InvalidDurationError` {#invaliddurationerror}

**Extends:** [`SurrealError`](#surrealerror)

Thrown when a `Duration` string could not be parsed.

---

### `InvalidGeometryError` {#invalidgeometryerror}

**Extends:** [`SurrealError`](#surrealerror)

Thrown when geometry data is invalid.

---

### `InvalidTableError` {#invalidtableerror}

**Extends:** [`SurrealError`](#surrealerror)

Thrown when a table or record ID string is invalid.

## Error kind constants

### `ErrorKind` {#errorkind}

Known error kinds returned by the SurrealDB server. Use these constants for matching against `ServerError.kind`.

```python
from surrealdb import ErrorKind
```

| Constant | Value |
|----------|-------|
| `ErrorKind.VALIDATION` | `"Validation"` |
| `ErrorKind.CONFIGURATION` | `"Configuration"` |
| `ErrorKind.THROWN` | `"Thrown"` |
| `ErrorKind.QUERY` | `"Query"` |
| `ErrorKind.SERIALIZATION` | `"Serialization"` |
| `ErrorKind.NOT_ALLOWED` | `"NotAllowed"` |
| `ErrorKind.NOT_FOUND` | `"NotFound"` |
| `ErrorKind.ALREADY_EXISTS` | `"AlreadyExists"` |
| `ErrorKind.CONNECTION` | `"Connection"` |
| `ErrorKind.INTERNAL` | `"Internal"` |

### Detail kind constants {#detail-kind-constants}

Each server error kind has associated detail kind constants for more specific matching.

| Class | Constants |
|-------|-----------|
| `AuthDetailKind` | `TOKEN_EXPIRED`, `SESSION_EXPIRED`, `INVALID_AUTH`, `UNEXPECTED_AUTH`, `MISSING_USER_OR_PASS`, `NO_SIGNIN_TARGET`, `INVALID_PASS`, `TOKEN_MAKING_FAILED`, `INVALID_SIGNUP`, `INVALID_ROLE`, `NOT_ALLOWED` |
| `ValidationDetailKind` | `PARSE`, `INVALID_REQUEST`, `INVALID_PARAMS`, `NAMESPACE_EMPTY`, `DATABASE_EMPTY`, `INVALID_PARAMETER`, `INVALID_CONTENT`, `INVALID_MERGE` |
| `ConfigurationDetailKind` | `LIVE_QUERY_NOT_SUPPORTED`, `BAD_LIVE_QUERY_CONFIG`, `BAD_GRAPHQL_CONFIG` |
| `QueryDetailKind` | `NOT_EXECUTED`, `TIMED_OUT`, `CANCELLED` |
| `SerializationDetailKind` | `SERIALIZATION`, `DESERIALIZATION` |
| `NotAllowedDetailKind` | `SCRIPTING`, `AUTH`, `METHOD`, `FUNCTION`, `TARGET` |
| `NotFoundDetailKind` | `METHOD`, `SESSION`, `TABLE`, `RECORD`, `NAMESPACE`, `DATABASE`, `TRANSACTION` |
| `AlreadyExistsDetailKind` | `SESSION`, `TABLE`, `RECORD`, `NAMESPACE`, `DATABASE` |
| `ConnectionDetailKind` | `UNINITIALISED`, `ALREADY_CONNECTED` |

## See also

- [Error handling concept](/docs/reference/python/concepts/error-handling.md) for patterns and best practices
- [Surreal API reference](/docs/reference/python/api/core/surreal.md) for methods that may throw errors
- [Python types](/docs/reference/python/api/types/) for the `Value` and `Tokens` types
