# Error handling

The Java SDK provides a structured exception hierarchy for handling errors from the database and SDK.

The Java SDK provides a structured exception hierarchy for handling errors from the database and SDK. All exceptions extend [`SurrealException`](/docs/reference/java/api/errors.md#surreal-exception), which is an unchecked exception. Server-returned errors are represented as [`ServerException`](/docs/reference/java/api/errors.md#server-exception) subclasses with typed error details.

The error kinds, wire codes and structured shape behind these are documented once in [Errors](/docs/reference/rest-api/errors.md), which applies to every SDK and protocol.

## API references

<table>
	<thead>
		<tr>
			<th scope="col">Error class</th>
			<th scope="col">Description</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td scope="row" data-label="Error class"><a href="/docs/reference/java/api/errors.md#surreal-exception"><code>SurrealException</code></a></td>
			<td scope="row" data-label="Description">Base exception for all SDK errors</td>
		</tr>
		<tr>
			<td scope="row" data-label="Error class"><a href="/docs/reference/java/api/errors.md#server-exception"><code>ServerException</code></a></td>
			<td scope="row" data-label="Description">Base for server-returned errors</td>
		</tr>
		<tr>
			<td scope="row" data-label="Error class"><a href="/docs/reference/java/api/errors.md#not-found-exception"><code>NotFoundException</code></a></td>
			<td scope="row" data-label="Description">Thrown when a resource is not found</td>
		</tr>
		<tr>
			<td scope="row" data-label="Error class"><a href="/docs/reference/java/api/errors.md#not-allowed-exception"><code>NotAllowedException</code></a></td>
			<td scope="row" data-label="Description">Thrown when an operation is not permitted</td>
		</tr>
		<tr>
			<td scope="row" data-label="Error class"><a href="/docs/reference/java/api/errors.md#query-exception"><code>QueryException</code></a></td>
			<td scope="row" data-label="Description">Thrown when a query fails</td>
		</tr>
		<tr>
			<td scope="row" data-label="Error class"><a href="/docs/reference/java/api/errors.md#already-exists-exception"><code>AlreadyExistsException</code></a></td>
			<td scope="row" data-label="Description">Thrown when a resource already exists</td>
		</tr>
	</tbody>
</table>

## Exception hierarchy

[`SurrealException`](/docs/reference/java/api/errors.md#surreal-exception) is the base class for all exceptions thrown by the SDK. It extends `RuntimeException`, so exceptions are unchecked. [`ServerException`](/docs/reference/java/api/errors.md#server-exception) extends `SurrealException` and represents errors returned by the SurrealDB server. Specific exception types extend `ServerException` for common error categories.

- [`SurrealException`](/docs/reference/java/api/errors.md#surreal-exception) - base for all SDK errors
  - [`ServerException`](/docs/reference/java/api/errors.md#server-exception) - base for server-returned errors
    - [`NotFoundException`](/docs/reference/java/api/errors.md#not-found-exception) - resource not found
    - [`NotAllowedException`](/docs/reference/java/api/errors.md#not-allowed-exception) - operation not permitted
    - [`QueryException`](/docs/reference/java/api/errors.md#query-exception) - query execution failed
    - [`AlreadyExistsException`](/docs/reference/java/api/errors.md#already-exists-exception) - resource already exists

## Catching server errors

All server errors are [`ServerException`](/docs/reference/java/api/errors.md#server-exception) subclasses. Catch specific exceptions first for targeted handling, then fall back to `ServerException` for unexpected server errors.

```java
try {
    db.query("SELECT * FROM protected_table");
} catch (NotAllowedException e) {
    System.err.println("Permission denied: " + e.getMessage());
} catch (QueryException e) {
    System.err.println("Query failed: " + e.getMessage());
} catch (ServerException e) {
    System.err.println("Server error: " + e.getMessage());
}
```

## Inspecting error details

[`ServerException`](/docs/reference/java/api/errors.md#server-exception) provides methods for inspecting the error returned by the server.

- `.getKind()` - returns the error kind as a `String`
- `.getKindEnum()` - returns the error kind as an [`ErrorKind`](/docs/reference/java/api/errors.md#error-kind) enum value
- `.getDetails()` - returns additional error details

The [`ErrorKind`](/docs/reference/java/api/errors.md#error-kind) enum includes: `VALIDATION`, `CONFIGURATION`, `THROWN`, `QUERY`, `SERIALIZATION`, `NOT_ALLOWED`, `NOT_FOUND`, `ALREADY_EXISTS`, `CONNECTION`, `INTERNAL`, and `UNKNOWN`.

```java
try {
    db.query("INVALID QUERY");
} catch (ServerException e) {
    ErrorKind kind = e.getKindEnum();
    String details = e.getDetails();
    System.err.println(kind + ": " + details);
}
```

## Traversing error chains

Server errors can have nested causes. [`ServerException`](/docs/reference/java/api/errors.md#server-exception) provides methods for walking the cause chain to find a specific error type.

- `.getServerCause()` - returns the underlying `ServerException` cause, if any
- `.hasKind(kind)` - checks whether this error or any cause matches the given `ErrorKind`
- `.findCause(kind)` - searches the cause chain and returns the first `ServerException` matching the given `ErrorKind`

```java
try {
    db.query("SELECT * FROM users");
} catch (ServerException e) {
    if (e.hasKind(ErrorKind.NOT_ALLOWED)) {
        ServerException cause = e.findCause(ErrorKind.NOT_ALLOWED);
        System.err.println("Permission error: " + cause.getDetails());
    }
}
```

## Handling specific error types

Specific exception subclasses expose additional context about the error.

[`NotFoundException`](/docs/reference/java/api/errors.md#not-found-exception) provides `.getTableName()` and `.getRecordId()` to identify the missing resource.

```java
try {
    Optional<Value> user = db.select(new RecordId("users", "nonexistent"));
} catch (NotFoundException e) {
    System.err.println("Table: " + e.getTableName());
    System.err.println("Record: " + e.getRecordId());
}
```

[`NotAllowedException`](/docs/reference/java/api/errors.md#not-allowed-exception) provides `.isTokenExpired()` and `.isInvalidAuth()` to distinguish authentication failures.

```java
try {
    db.query("SELECT * FROM protected");
} catch (NotAllowedException e) {
    if (e.isTokenExpired()) {
        System.err.println("Token expired, re-authenticate");
    } else if (e.isInvalidAuth()) {
        System.err.println("Invalid credentials");
    }
}
```

[`QueryException`](/docs/reference/java/api/errors.md#query-exception) provides `.isTimedOut()` and `.isCancelled()` to identify query lifecycle issues.

```java
try {
    db.query("SELECT * FROM large_table");
} catch (QueryException e) {
    if (e.isTimedOut()) {
        System.err.println("Query timed out");
    } else if (e.isCancelled()) {
        System.err.println("Query was cancelled");
    }
}
```

## Learn more

- [Errors API reference](/docs/reference/java/api/errors.md) for complete exception class documentation
- [Connecting to SurrealDB](/docs/reference/java/concepts/connecting-to-surrealdb.md) for connection error scenarios
- [Authentication](/docs/reference/java/concepts/authentication.md) for authentication error scenarios
- [SurrealQL THROW](/docs/reference/query-language/statements/throw.md) for throwing custom errors from queries
