Every exception that the Python SDK raises for a failed operation is a subclass of SurrealError. This page lists each class with what raises it and the properties it carries. A mistake in the arguments of a call raises Python's own TypeError or ValueError instead. Error handling shows how to catch these errors and which ones to retry.
Every class on this page is exported from surrealdb:
from surrealdb import NotFoundError, ServerError, SurrealError, TransportErrorThe classes form this hierarchy:
SurrealError
├── ServerError
│ ├── ValidationError
│ ├── ConfigurationError
│ ├── ThrownError
│ ├── QueryError
│ ├── SerializationError
│ ├── NotAllowedError
│ ├── NotFoundError
│ ├── AlreadyExistsError
│ └── InternalError
├── TransportError
│ ├── ConnectionUnavailableError
│ ├── TransportTimeoutError
│ └── HttpStatusError
├── UnsupportedFeatureError
│ └── UnavailableFeatureError
├── UnsupportedVersionError
├── UnsupportedEngineError
├── InvalidUrlError
├── ImportFailedError
├── UnexpectedResponseError
├── InvalidRecordIdError
├── InvalidDurationError
├── InvalidGeometryError
└── InvalidTableErrorSource: errors.py
Base error
SurrealError
Extends: Exception
Base class for every error that the SDK raises. Catch it to handle any failure of an SDK call in one place.
from surrealdb import SurrealError, Table
try:
people = db.select(Table("person")).execute()
except SurrealError as error:
print(f"{type(error).__name__}: {error}")Server errors
A server error is a failure that the server reported in its structured error format. The SDK chooses the class from the kind of the error, as listed under ErrorKind. SurrealDB 2.x sends no kind, so on a 2.x server every server error is an InternalError. Errors describes the kinds, details and wire codes that the server sends.
ServerError
Extends: SurrealError
A failure that the server reported, with its kind, details and cause chain. Every class in this section extends it. A kind without a class of its own, such as Connection or a kind added by a newer server, arrives as a ServerError with its kind set. str(error) is the message of the server, and repr() shows the class and kind, such as ThrownError(kind='Thrown', message='An error occurred: nope').
ServerError(kind, message, code=0, details=None, cause=None)The SDK builds server errors from the answers of the server. Construct one yourself only to test your own error handling.
Properties
Property | Type | Description |
|---|---|---|
kind | str | The kind of the error, such as "NotAllowed"or "NotFound". Compare it with the ErrorKindconstants. |
code | int | The JSON-RPC wire code, kept for compatibility. 0when the server sends none, as for an error in a statement. |
details | dict[str, Any] | None | The reason for the error, as a dict with a "kind"key and, for a reason that carries values, a "details"key. Nonewhen the server sends no reason. The properties of each subclass read it for you. |
server_cause | ServerError | None | The server error that caused this one, if the server sent one. It is also the __cause__of the exception. |
Methods
.has_kind()
Checks whether this error, or any error in its chain of causes, has the given kind.
error.has_kind(kind)Parameter | Type | Description |
|---|---|---|
kind | str | An error kind, such as ErrorKind.NOT_FOUND. |
Returns: bool
.find_cause()
Returns the first error with the given kind: this error, or the first error in its chain of causes.
error.find_cause(kind)Parameter | Type | Description |
|---|---|---|
kind | str | An error kind, such as ErrorKind.NOT_FOUND. |
Returns: ServerError | None
from surrealdb import ErrorKind, ServerError, Table
try:
db.select(Table("post_archive")).execute()
except ServerError as error:
print(error.has_kind(ErrorKind.NOT_FOUND))
print(error.find_cause(ErrorKind.NOT_FOUND).details)True
{'details': {'name': 'post_archive'}, 'kind': 'Table'} ValidationError
Extends: ServerError
A request or a query that is not valid: a parse error, invalid parameters, or a value that does not fit the schema.
| Property | Type | Description |
|---|---|---|
is_parse_error | bool | Whether the reason in details is Parse. SurrealDB 3.x and 4.0 send a parse error in a query without details, so it is False for that error. |
parameter_name | str | None | The name of the invalid parameter, for the reason InvalidParameter |
ConfigurationError
Extends: ServerError
A feature or a configuration that the server does not support, such as live queries or GraphQL.
| Property | Type | Description |
|---|---|---|
is_live_query_not_supported | bool | Whether the server does not support live queries |
ThrownError
Extends: ServerError
An error raised by a THROW statement in SurrealQL, including one in a function.
from surrealdb import ThrownError
try:
db.query("THROW 'Order not found'").execute()
except ThrownError as error:
print(error)An error occurred: Order not found QueryError
Extends: ServerError
A query that did not finish: it timed out, was cancelled, was not run because an earlier statement in its transaction failed, or conflicted with another transaction.
| Property | Type | Description |
|---|---|---|
is_not_executed | bool | Whether the statement was not run, such as one in a transaction after a statement that failed |
is_timed_out | bool | Whether the statement ran past its TIMEOUT |
is_cancelled | bool | Whether the statement was cancelled |
is_transaction_conflict | bool | Whether the transaction conflicted with another one. The server marks this reason as one that can be retried. |
timeout | Duration | None | The timeout that the statement ran past, or None for a reason other than TimedOut |
from surrealdb import QueryError
try:
db.query("SELECT * FROM sleep(500ms) TIMEOUT 50ms").execute()
except QueryError as error:
print(error.is_timed_out, error.timeout)True 50msA QueryError timeout is enforced by the server. When the SDK stops waiting for a reply, it raises TransportTimeoutError instead.
SerializationError
Extends: ServerError
A value that the server could not serialise or deserialise.
| Property | Type | Description |
|---|---|---|
is_deserialization | bool | Whether the failure was in deserialisation |
NotAllowedError
Extends: ServerError
A refused authentication, or a method, function or target that the session may not use.
| Property | Type | Description |
|---|---|---|
is_token_expired | bool | Whether the authentication token has expired |
is_invalid_auth | bool | Whether the credentials or the token are invalid |
is_scripting_blocked | bool | Whether embedded scripting is not allowed |
method_name | str | None | The name of the method that is not allowed |
function_name | str | None | The name of the function that is not allowed |
target_name | str | None | The name of the target that is not allowed |
from surrealdb import NotAllowedError, Surreal
with Surreal("ws://localhost:8000") as db:
try:
db.signin({"username": "root", "password": "wrong"})
except NotAllowedError as error:
print(error.is_invalid_auth, error.is_token_expired)True FalseA record user whose SIGNIN clause matches no record gets NotFoundError with the message No record was returned. A table permission that denies a statement does not raise at all: the statement returns an empty result.
NotFoundError
Extends: ServerError
A resource that does not exist, such as a table, a namespace, a database, a method or a session. From SurrealDB 3.0, a statement or a live query on a table that does not exist raises it, also for one record of that table. select() of a record that does not exist, in a table that does, returns None and does not raise.
| Property | Type | Description |
|---|---|---|
table_name | str | None | The name of the table that does not exist |
record_id | str | None | The id of the record that does not exist |
method_name | str | None | The name of the method that does not exist |
namespace_name | str | None | The name of the namespace that does not exist |
database_name | str | None | The name of the database that does not exist |
session_id | str | None | The id of the session that does not exist |
from surrealdb import NotFoundError, Table
try:
db.select(Table("post_archive")).execute()
except NotFoundError as error:
print(error.table_name, "|", error)post_archive | The table 'post_archive' does not exist AlreadyExistsError
Extends: ServerError
A resource that already exists, such as a record, a table or a namespace.
| Property | Type | Description |
|---|---|---|
record_id | str | None | The id of the record that already exists, such as "person:tobie" |
table_name | str | None | The name of the table that already exists |
session_id | str | None | The id of the session that already exists |
namespace_name | str | None | The name of the namespace that already exists |
database_name | str | None | The name of the database that already exists |
InternalError
Extends: ServerError
An internal or unexpected failure on the server, and the class for every server error from SurrealDB 2.x, which sends no kind. SurrealDB 3.x and 4.0 also report some mistakes in a query with this kind, such as a function called with the wrong number of arguments, a function that does not exist, and a bucket that does not exist.
SurrealDBMethodError
Another name for ServerError, kept so that code written for earlier versions of the SDK still runs. except SurrealDBMethodError catches every server error. Use ServerError in new code.
Transport errors
A transport error means that a request got no answer from the server in the structured error format: it could not be sent, no reply arrived in time, or the reply was an HTTP error status. The same request can succeed when you send it again, but the error does not tell you whether the server ran it. Each transport error keeps the exception of the underlying library, such as a ConnectionRefusedError or a requests exception, as __cause__.
TransportError
Extends: SurrealError
Base class for the three transport errors below. Catch it to retry a request whatever the transport failure was.
ConnectionUnavailableError
Extends: TransportError
Raised when the server cannot be reached, when the socket closes while a request waits for its reply, and when a live subscription is read after the connection that served it was lost. A request that was in flight when the socket dropped is not sent again, because it may already have run. An asynchronous connection used from a second event loop also raises it.
from surrealdb import ConnectionUnavailableError, Surreal
db = Surreal("ws://localhost:8001")
try:
db.connect()
except ConnectionUnavailableError as error:
print(type(error.__cause__).__name__)ConnectionRefusedError TransportTimeoutError
Extends: TransportError
No reply arrived within the timeout of the connection, 30 seconds by default, or within a db.timeout() block. The server can still be running the request.
from surrealdb import TransportTimeoutError
try:
with db.timeout(0.5):
db.query("SLEEP 2s").execute()
except TransportTimeoutError as error:
print(error)timed out while query: no answer within 0.5s HttpStatusError
Extends: TransportError
An HTTP connection got a response with a status outside the 2xx range. SurrealDB answers some failures at the HTTP layer, with a plain body and no structured error, so a token that the server refuses over HTTP raises HttpStatusError with status 401, where a WebSocket connection raises NotAllowedError.
HttpStatusError(status, body, url)| Property | Type | Description |
|---|---|---|
status | int | The HTTP status code |
body | str | The body of the response as text, shortened if it is long. Empty when the response has no body. |
url | str | The URL that the request was sent to |
from surrealdb import HttpStatusError, Surreal
with Surreal("http://localhost:8000") as db:
try:
db.authenticate(token)
except HttpStatusError as error:
print(error)HTTP 401 from http://localhost:8000/rpc: There was a problem with authenticationHere token holds an access token that the server refuses.
SDK-side errors
These errors come from the SDK itself, without a request to the server, or from what the SDK found in an answer.
UnsupportedFeatureError
Extends: SurrealError
The type of connection does not have the feature. Sessions, client-side transactions and live queries need a WebSocket, so an HTTP connection raises this error for them. An embedded connection raises it for sessions, transactions, live queries, health(), export(), export_to() and import_().
from surrealdb import Surreal, UnsupportedFeatureError
with Surreal("http://localhost:8000") as db:
db.signin({"username": "root", "password": "secret"})
try:
session = db.new_session()
except UnsupportedFeatureError as error:
print(error)Multi-session and client-side transactions are only supported for WebSocket connections UnavailableFeatureError
Extends: UnsupportedFeatureError
The version of the server does not have the feature, such as sessions on SurrealDB 2.x. The SDK raises it before it sends anything, from the version that the server reported to the connection. Code that catches UnsupportedFeatureError also catches this class. To check first, call db.is_feature_supported().
UnavailableFeatureError(feature, version, since)| Property | Type | Description |
|---|---|---|
feature | str | The name of the feature, such as "sessions" |
version | str | The version of the server, such as "2.7.0" |
since | str | None | The first SurrealDB version with the feature, such as "3.0.0", or None when the server is too new for the feature rather than too old |
sessions needs SurrealDB 3.0.0 or later, but this server is 2.7.0. Upgrade the server, or avoid this feature on this connection. UnsupportedVersionError
Extends: SurrealError
The version of the server is outside the range that the SDK supports. That range is SurrealDB 2.x, 3.x and 4.x, from MINIMUM_VERSION up to, but not including, MAXIMUM_VERSION. Both constants are exported from surrealdb. A WebSocket connection checks the version when the socket opens, and an HTTP connection before its first request. Pre-release and nightly builds count as the release they lead up to. Pass version_check=False to the connection to connect anyway.
UnsupportedVersionError(version, minimum, maximum)| Property | Type | Description |
|---|---|---|
version | str | The version of the server |
minimum | str | The lowest supported version |
maximum | str | The first version that is not supported |
UnsupportedEngineError
Extends: SurrealError
Surreal() and AsyncSurreal() do not know the scheme of the URL. It is also raised for an embedded URL, such as mem://, when the surrealdb[embedded] package is not installed, and the message names the package to install.
UnsupportedEngineError(url)| Property | Type | Description |
|---|---|---|
url | str | The URL that was given |
from surrealdb import Surreal, UnsupportedEngineError
try:
db = Surreal("ftp://localhost:8000")
except UnsupportedEngineError as error:
print(error.url)ftp://localhost:8000 InvalidUrlError
Extends: SurrealError
A URL with a known scheme that cannot be parsed, such as one with a port out of range, a port that is not a number, or an IPv6 address without its closing bracket.
from surrealdb import InvalidUrlError, Surreal
try:
db = Surreal("ws://localhost:99999")
except InvalidUrlError as error:
print(error)Could not parse URL 'ws://localhost:99999': Port out of range 0-65535 ImportFailedError
Extends: SurrealError
Statements in an import_() failed. The other statements were applied. The message lists the first three failures.
ImportFailedError(failures)| Property | Type | Description |
|---|---|---|
failures | list[ServerError] | The error of each statement that failed, in order |
1 statement(s) in the import failed: Database record `person:tobie` already exists UnexpectedResponseError
Extends: SurrealError
The SDK could not read an answer of the server: a response that does not decode, or a value that is not one SurrealDB sends. Mapping records with into= also raises it when a record has a field that the model does not accept.
InvalidRecordIdError
Extends: SurrealError
A record id given as text that the SDK cannot use: a StringRecordId without a :, a StringRecordId that names a range such as "person:1..3", or text without a : given to RecordID.parse().
InvalidDurationError
Extends: SurrealError
Text that Duration.parse() cannot read. It takes only parts of the form <digits><unit>, such as "1h30m", so "1.5s", "-1s" and "1H" raise this error.
InvalidGeometryError
Extends: SurrealError
Geometry data that is not valid, such as a ring of a GeometryPolygon with fewer than four points or with a last point that differs from its first.
InvalidTableError
Extends: SurrealError
A table or record id string that is not valid. No method of a connection raises it in SDK 3.0, because a plain string is always a table name. It stays exported, so code that catches it still runs.
Error kind constants
ErrorKind
The kinds that the server gives its errors, as string constants to compare with ServerError.kind or to pass to has_kind() and find_cause().
from surrealdb import ErrorKind| Constant | Value | Exception class |
|---|---|---|
ErrorKind.VALIDATION | "Validation" | ValidationError |
ErrorKind.CONFIGURATION | "Configuration" | ConfigurationError |
ErrorKind.THROWN | "Thrown" | ThrownError |
ErrorKind.QUERY | "Query" | QueryError |
ErrorKind.SERIALIZATION | "Serialization" | SerializationError |
ErrorKind.NOT_ALLOWED | "NotAllowed" | NotAllowedError |
ErrorKind.NOT_FOUND | "NotFound" | NotFoundError |
ErrorKind.ALREADY_EXISTS | "AlreadyExists" | AlreadyExistsError |
ErrorKind.CONNECTION | "Connection" | ServerError |
ErrorKind.INTERNAL | "Internal" | InternalError |
Detail kind constants
The details of a server error name the reason within its kind, as {"kind": ..., "details": ...}. These classes hold the reasons as constants. The value of each constant is the reason as the server sends it, in CamelCase: QueryDetailKind.TRANSACTION_CONFLICT is "TransactionConflict".
| Class | Reasons within | Constants |
|---|---|---|
ValidationDetailKind | Validation | PARSE, INVALID_REQUEST, INVALID_PARAMS, NAMESPACE_EMPTY, DATABASE_EMPTY, INVALID_PARAMETER, INVALID_CONTENT, INVALID_MERGE |
ConfigurationDetailKind | Configuration | LIVE_QUERY_NOT_SUPPORTED, BAD_LIVE_QUERY_CONFIG, BAD_GRAPHQL_CONFIG |
QueryDetailKind | Query | NOT_EXECUTED, TIMED_OUT, CANCELLED, TRANSACTION_CONFLICT |
SerializationDetailKind | Serialization | SERIALIZATION, DESERIALIZATION |
NotAllowedDetailKind | NotAllowed | SCRIPTING, AUTH, METHOD, FUNCTION, TARGET |
AuthDetailKind | The Auth reason of NotAllowed | 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 |
NotFoundDetailKind | NotFound | METHOD, SESSION, TABLE, RECORD, NAMESPACE, DATABASE, TRANSACTION |
AlreadyExistsDetailKind | AlreadyExists | SESSION, TABLE, RECORD, NAMESPACE, DATABASE |
ConnectionDetailKind | Connection | UNINITIALISED, ALREADY_CONNECTED |
details is None when the server sends no reason, so check it before you read it. A reason without a constant here, such as one added by a newer server, still arrives in details.
from surrealdb import NotFoundDetailKind, NotFoundError, Table
try:
db.select(Table("post_archive")).execute()
except NotFoundError as error:
details = error.details or {}
if details.get("kind") == NotFoundDetailKind.TABLE:
print("Missing table:", details["details"]["name"])Missing table: post_archiveA failed sign-in nests one reason in another: {"kind": "Auth", "details": {"kind": "InvalidAuth"}}. The properties of each class, such as NotAllowedError.is_invalid_auth, read the nested reasons for you.
See also
Error handling for patterns and examples
Errors for the error kinds, details and wire codes of every protocol
Surreal API reference for the methods that raise these errors
Python types for the
ValueandTokenstypes