# Error handling

Learn how to handle errors in the Developer SDK. The client provides actionable error messages with clear causes and recovery suggestions.

## Error philosophy

The Developer SDK follows these principles:

1.  **Actionable messages** — Every error tells you what went wrong and how to fix it
2.  **Typed exceptions** — Catch specific error types, not generic exceptions
3.  **Recovery hints** — Errors include suggestions for resolution
4.  **No silent failures** — Single-key operations either succeed or throw. Batch and stream operations surface per-record results explicitly: by default each record’s outcome is embedded in the returned stream as a `RecordResult` (errors and successes side-by-side), and you can opt into a strict-throw mode if you prefer (see [Error handling](https://aerospike.com/docs/develop/client/sdk/concepts/error-handling/)).

## Exception hierarchy

All Developer SDK exceptions inherit from a base exception:

```plaintext
AerospikeException (RuntimeException)

│

│   // Record-level errors

├── RecordNotFoundException                     KEY_NOT_FOUND_ERROR (2)

├── RecordExistsException                       KEY_EXISTS_ERROR (5)

├── GenerationException                         GENERATION_ERROR (3)

├── FilteredException                           FILTERED_OUT (27)

├── RecordTooBigException                       RECORD_TOO_BIG (13)

│

│   // Bin-level errors

├── BinException                                BIN_NAME_TOO_LONG (21)

│   ├── BinExistsException                      BIN_EXISTS_ERROR (6)

│   ├── BinNotFoundException                    BIN_NOT_FOUND (17)

│   ├── BinTypeException                        BIN_TYPE_ERROR (12)

│   └── BinOpInvalidException                   OP_NOT_APPLICABLE (26)

│

│   // CDT element-level errors

├── ElementException

│   ├── ElementNotFoundException                ELEMENT_NOT_FOUND (23)

│   └── ElementExistsException                  ELEMENT_EXISTS (24)

│

│   // Transaction / MRT errors

├── TransactionException                        TXN_ALREADY_ABORTED (-19)

│   │                                           TXN_ALREADY_COMMITTED (-18)

│   │                                           MRT_BLOCKED (120)

│   │                                           MRT_VERSION_MISMATCH (121)

│   │                                           MRT_EXPIRED (122)

│   │                                           MRT_TOO_MANY_WRITES (123)

│   │                                           MRT_COMMITTED (124)

│   │                                           MRT_ABORTED (125)

│   │                                           MRT_ALREADY_LOCKED (126)

│   │                                           MRT_MONITOR_EXISTS (127)

│   └── Commit                                  TXN_FAILED (-17)

│

│   // Security errors

├── SecurityException                           ILLEGAL_STATE (56)

│   │                                           USER_ALREADY_EXISTS (61)

│   │                                           FORBIDDEN_PASSWORD (64)

│   │                                           SECURITY_NOT_ENABLED (52)

│   │                                           SECURITY_NOT_SUPPORTED (51)

│   │                                           SECURITY_SCHEME_NOT_SUPPORTED (53)

│   │                                           EXPIRED_SESSION (66)

│   │                                           INVALID_ROLE (70)

│   │                                           ROLE_ALREADY_EXISTS (71)

│   │                                           INVALID_PRIVILEGE (72)

│   │                                           INVALID_WHITELIST (73)

│   ├── AuthenticationException                 INVALID_USER (60)

│   │                                           INVALID_PASSWORD (62)

│   │                                           INVALID_CREDENTIAL (65)

│   │                                           EXPIRED_PASSWORD (63)

│   │                                           NOT_AUTHENTICATED (80)

│   └── AuthorizationException                  ROLE_VIOLATION (81)

│                                               NOT_WHITELISTED (82)

│

│   // Quota errors

├── QuotaException                              QUOTA_EXCEEDED (83)

│                                               QUOTAS_NOT_ENABLED (74)

│                                               INVALID_QUOTA (75)

│

│   // Capacity / resource exhaustion errors

├── CapacityException                           SERVER_MEM_ERROR (8)

│   │                                           DEVICE_OVERLOAD (18)

│   │                                           NO_MORE_CONNECTIONS (-7)

│   │                                           ASYNC_QUEUE_FULL (-9)

│   │                                           BATCH_QUEUES_FULL (152)

│   │                                           BATCH_MAX_REQUESTS_EXCEEDED (151)

│   ├── KeyBusyException                        KEY_BUSY (14)

│   └── Connection                              SERVER_NOT_AVAILABLE (-8)

│

│   // Secondary index errors

├── IndexException                              INDEX_ALREADY_EXISTS (200)

│                                               INDEX_NOTFOUND (201)

│                                               INDEX_OOM (202)

│                                               INDEX_NOTREADABLE (203)

│                                               INDEX_GENERIC (204)

│                                               INDEX_NAME_MAXLEN (205)

│                                               INDEX_MAXCOUNT (206)

│

│   // Query / scan errors

├── QueryException                              QUERY_ABORTED (210)

│                                               QUERY_QUEUEFULL (211)

│                                               QUERY_TIMEOUT (212)

│                                               QUERY_GENERIC (213)

│                                               SCAN_ABORT (15)

│

│   // Batch errors

├── BatchException                              BATCH_FAILED (-16)

│                                               BATCH_DISABLED (150)

│

│   // UDF errors

├── UdfException                                UDF_BAD_RESPONSE (100)

│

│   // Client infrastructure errors

├── Timeout                                     TIMEOUT (9)

├── InvalidNode                                 INVALID_NODE_ERROR (-3)

├── Serialize                                   SERIALIZE_ERROR (-10)

├── Parse                                       PARSE_ERROR (-2)

├── InvalidNamespace                            INVALID_NAMESPACE (20)

├── QueryTerminated                             QUERY_TERMINATED (-5)

├── Backoff                                     MAX_ERROR_RATE (-12)

│

│   // Codes with no dedicated subclass (plain AerospikeException)

│   CLIENT_ERROR (-1)                           Generic client error

│   NO_RESPONSE (-15)                           No response received from server

│   MAX_RETRIES_EXCEEDED (-11)                  Max retries limit reached

│   SCAN_TERMINATED (-4)                        Scan was terminated by user

│   OK (0)                                      Operation was successful

│   SERVER_ERROR (1)                            Unknown server failure

│   PARAMETER_ERROR (4)                         Bad parameter(s) in operation call

│   CLUSTER_KEY_MISMATCH (7)                    Expected cluster was not received

│   ALWAYS_FORBIDDEN (10)                       Operation not allowed in current configuration

│   PARTITION_UNAVAILABLE (11)                  Partition is unavailable

│   UNSUPPORTED_FEATURE (16)                    Unsupported server feature

│   KEY_MISMATCH (19)                           Key type mismatch

│   FAIL_FORBIDDEN (22)                         Operation not allowed at this time

│   ENTERPRISE_ONLY (25)                        Enterprise feature on Community server

│   LOST_CONFLICT (28)                          Write lost conflict to XDR

│   XDR_KEY_BUSY (32)                           Write blocked by XDR shipping

│   QUERY_END (50)                              No more records left for query

│   INVALID_COMMAND (54)                        Invalid administration command

│   INVALID_FIELD (55)                          Invalid administration field
```

## Common exceptions

### RecordExistsException

Thrown when attempting to create a record that already exists.

-   [Java](#tab-panel-2980)
-   [Python](#tab-panel-2981)

```java
import com.aerospike.client.sdk.AerospikeException.RecordExistsException;

try {

    session.insert(users)

        .bins("name")

        .id("user-1").values("Alice")

        .execute();

} catch (RecordExistsException e) {

    System.out.println("Record already exists");

    System.out.println("Details: " + e.getMessage());

    // Option 1: Update instead

    session.upsert(users)

        .bins("name")

        .id("user-1").values("Alice")

        .execute();

    // Option 2: Skip silently (if that's acceptable)

}
```

```python
from aerospike_sdk import AerospikeError

from aerospike_async.exceptions import ResultCode

try:

    await session.insert(key=users.id("user-1")).put({"name": "Alice"}).execute()

except AerospikeError as e:

    if e.result_code != ResultCode.KEY_EXISTS_ERROR:

        raise

    print("Record already exists")

    # Option 1: Update instead

    await session.upsert(key=users.id("user-1")).put({"name": "Alice"}).execute()

    # Option 2: Skip silently (if that's acceptable)
```

### RecordNotFoundException

Thrown when a required record doesn’t exist.

-   [Java](#tab-panel-2982)
-   [Python](#tab-panel-2983)

```java
import com.aerospike.client.sdk.Record;

import com.aerospike.client.sdk.RecordStream;

import com.aerospike.client.sdk.AerospikeException.GenerationException;

// Missing record: stream has no OK row

RecordStream stream = session.query(users.id("nonexistent")).execute();

if (stream.getFirst().isEmpty()) {

    System.out.println("Record not found");

}

// Alternative: Optional when present

RecordStream stream2 = session.query(users.id("user-1")).execute();

Optional<Record> maybeUser = stream2.getFirst()

    .filter(RecordResult::isOk)

    .map(RecordResult::recordOrThrow);

if (maybeUser.isEmpty()) {

    // Handle gracefully without exception

}
```

```python
stream = await session.query(users.id("user-1")).execute()

try:

    row = await stream.first()

    if row is None:

        print("Record not found")

        # Handle missing record...

    else:

        _ = row.record_or_raise()

finally:

    stream.close()
```

### Generation mismatch (`GenerationException`)

Thrown when an optimistic lock check fails—the record was modified since you read it. In Java this surfaces as `AerospikeException.GenerationException`.

-   [Java](#tab-panel-2984)
-   [Python](#tab-panel-2985)

```java
import com.aerospike.client.sdk.Record;

import com.aerospike.client.sdk.RecordResult;

import com.aerospike.client.sdk.RecordStream;

// Read the record

Record user;

try (RecordStream read = session.query(users.id("user-1")).execute()) {

    user = read.getFirstRecord();

}

int originalGen = user.getGeneration();

// ... time passes, another process modifies the record ...

try {

    // Try to update with generation check

    session.update(users.id("user-1"))

        .bin("balance").setTo(200.00)

        .ensureGenerationIs(originalGen)

        .execute();

} catch (GenerationException e) {

    System.out.println("Record was modified by another process");

    System.out.println("Details: " + e.getMessage());

    // Re-read and retry

    Record fresh;

    try (RecordStream read2 = session.query(users.id("user-1")).execute()) {

        fresh = read2.getFirstRecord();

    }

    // ... recalculate and retry update ...

}
```

```python
from aerospike_sdk import GenerationError

# Read the record

stream = await session.query(users.id("user-1")).execute()

row = await stream.first_or_raise()

user = row.record_or_raise()

stream.close()

original_gen = user.generation

# ... time passes, another process modifies the record ...

try:

    # Try to update with generation check

    await (

        session.update(users.id("user-1"))

        .bin("balance").set_to(200.00)

        .ensure_generation_is(original_gen)

        .execute()

    )

except GenerationError as e:

    print("Record was modified by another process")

    print(f"Details: {e}")

    # Re-read and retry

    stream = await session.query(users.id("user-1")).execute()

    fresh = (await stream.first_or_raise()).record_or_raise()

    stream.close()

    # ... recalculate and retry update ...
```

### TimeoutException

Thrown when an operation exceeds its configured timeout.

-   [Java](#tab-panel-2986)
-   [Python](#tab-panel-2987)

```java
import com.aerospike.client.sdk.RecordResult;

import com.aerospike.client.sdk.RecordStream;

import com.aerospike.client.sdk.AerospikeException;

try {

    RecordStream stream = session.query(users)

        .where("$.status == 'active'")

        .execute();

    stream.forEach((RecordResult result) -> { /* ... */ });

} catch (AerospikeException.Timeout e) {

    // Timeouts expose both per-call and policy limits via the same type

    System.out.println("Timeout: " + e.getMessage());

    System.out.println("Socket timeout (ms): " + e.getSocketTimeout());

    System.out.println("Total timeout (ms): " + e.getTotalTimeout());

    System.out.println("Node: " + e.getNode());

}
```

```python
from aerospike_sdk import TimeoutError

try:

    stream = await session.query(users).where("$.status == 'active'").execute()

    async for row in stream:

        pass

    stream.close()

except TimeoutError as e:

    print(f"Timeout: {e}")
```

### ConnectionException

Thrown when there’s a problem connecting to the cluster.

-   [Java](#tab-panel-2988)
-   [Python](#tab-panel-2989)

```java
import com.aerospike.client.sdk.Cluster;

import com.aerospike.client.sdk.ClusterDefinition;

import com.aerospike.client.sdk.AerospikeException.Connection;

try {

    Cluster cluster = new ClusterDefinition("nonexistent.example.com", 3000).connect();

} catch (Connection e) {

    System.out.println("Cannot connect to cluster");

    System.out.println("Details: " + e.getMessage());

}
```

```python
from aerospike_sdk import ClusterDefinition, ConnectionError

try:

    cluster = await ClusterDefinition("nonexistent.example.com", 3000).connect()

except ConnectionError as e:

    print("Cannot connect to cluster")

    print(str(e))
```

### Invalid AEL / filter text

Malformed filter strings fail while building or executing the query (exact exception type may vary by release).

-   [Java](#tab-panel-2990)
-   [Python](#tab-panel-2991)

```java
import com.aerospike.client.sdk.RecordStream;

try {

    RecordStream stream = session.query(users)

        .where("$.age >= 21")  // malformed expression text

        .execute();

    stream.close();

} catch (RuntimeException e) {

    System.out.println("Invalid filter expression: " + e.getMessage());

}
```

```python
try:

    stream = await session.query(users).where("$.age > > 21").execute()

    stream.close()

except Exception as e:

    print(f"Invalid filter expression: {e}")
```

## Actionable error messages

Every Developer SDK exception includes:

| Property | Description |
| --- | --- |
| `message` | Human-readable description of what went wrong |
| `errorCode` / `result_code` | Code for programmatic handling (`getResultCode()` in Java; `result_code` on `AerospikeError` in Python) |
| `context` | Additional details (key, node, query, etc.) (Java) |

-   [Java](#tab-panel-2992)
-   [Python](#tab-panel-2993)

```java
try {

    session.insert(users)

        .bins("name")

        .id("user-1").values("Alice")

        .execute();

} catch (AerospikeException e) {

    System.out.println("Error: " + e.getMessage());

    System.out.println("Code: " + e.getResultCode());

    // Log structured error for monitoring

    logger.error("Aerospike error", Map.of(

        "code", e.getResultCode(),

        "message", e.getMessage(),

        "node", e.getNode()

    ));

}
```

```python
from aerospike_sdk import AerospikeError

try:

    await session.insert(key=users.id("user-1")).put({"name": "Alice"}).execute()

except AerospikeError as e:

    print(f"Error: {e}")

    print(f"Result code: {e.result_code}")

    logger.error("Aerospike error", extra={

        "result_code": str(e.result_code),

        "message": str(e),

    })
```

## Retry strategies

The Aerospike Developer SDKs feature automatic retry functionality. See the [Behaviors](https://aerospike.com/docs/develop/client/sdk/concepts/behaviors/#retry-strategies) topic for more information.

### Retryable vs non-retryable errors

| Java | Python | Retryable | Reason |
| --- | --- | --- | --- |
| `AerospikeException.Timeout` | `TimeoutError` (from `aerospike_sdk`) | ✅ Yes | Transient network issue |
| `AerospikeException.Connection` | `ConnectionError` (from `aerospike_sdk`) | ✅ Yes | Node may recover |
| `AerospikeException.RecordExistsException` | `AerospikeError` with `result_code == KEY_EXISTS_ERROR` | ❌ No | Logic error, needs different approach |
| `AerospikeException.RecordNotFoundException` | `AerospikeError` with `result_code == KEY_NOT_FOUND_ERROR` | ❌ No | Record doesn’t exist |
| `AerospikeException.GenerationException` | `GenerationError` | ⚠️ Maybe | Re-read and recalculate first |
| `AuthenticationException` | `AuthenticationError` | ❌ No | Wrong credentials |
| Invalid AEL / `where(...)` text | Same (raised as `AerospikeError`) | ❌ No | Fix the query syntax |

## Mapping to classic client exceptions

If you’re migrating from the classic Aerospike client:

| Classic Client | Developer SDK (Java) | Notes |
| --- | --- | --- |
| `AerospikeException` | `AerospikeException` | Base class |
| `AerospikeException.Timeout` | `AerospikeException.Timeout` | Same semantic class |
| `AerospikeException.Connection` | `AerospikeException.Connection` | Same semantic class |
| Result code `KEY_EXISTS_ERROR` | `AerospikeException.RecordExistsException` | Typed exception |
| Result code `KEY_NOT_FOUND_ERROR` | `AerospikeException.RecordNotFoundException` | Typed exception |
| Result code `GENERATION_ERROR` | `AerospikeException.GenerationException` | Typed exception |

::: note
The above table shows a subset of SDK exceptions. For the complete list, see the [Exception hierarchy](https://aerospike.com/docs/develop/client/sdk/concepts/errors/#exception-hierarchy).
:::

Python SDK equivalents:

-   Base class `AerospikeError`.
-   Timeouts and connectivity map to `TimeoutError` and `ConnectionError` from `aerospike_sdk`.
-   Generation mismatches use `GenerationError`.
-   Result codes such as `KEY_EXISTS_ERROR` and `KEY_NOT_FOUND_ERROR` surface as `AerospikeError` with `result_code` set. Compare against `aerospike_async.exceptions.ResultCode` when you need to branch.

## Error handling best practices

### 1\. Catch specific exceptions

```java
import com.aerospike.client.sdk.RecordStream;

// ❌ Don't catch generic Exception

try {

    RecordStream s = session.query(users.id("user-1")).execute();

    s.getFirst().ifPresent(r -> { /* ... */ });

} catch (Exception e) {

    // Lost type information

}

// ✅ Do catch specific types

try {

    RecordStream s = session.query(users.id("user-1")).execute();

    s.getFirst().ifPresent(r -> { /* ... */ });

} catch (AerospikeException.Timeout e) {

    // Handle timeout specifically

} catch (AerospikeException.Connection e) {

    // Handle connection issues

} catch (AerospikeException e) {

    // Handle other Aerospike errors

}
```

### 2\. Don’t swallow errors silently

```java
// ❌ Silent failure hides bugs

try {

    session.insert(users)

        .bins("name")

        .id("user-1").values("Alice")

        .execute();

} catch (Exception e) {

    // Ignore

}

// ✅ Log or handle appropriately

try {

    session.insert(users)

        .bins("name")

        .id("user-1").values("Alice")

        .execute();

} catch (RecordExistsException e) {

    logger.info("User already exists, skipping");

} catch (AerospikeException e) {

    logger.error("Failed to create user", e);

    throw e;

}
```

### 3\. Use Optional for expected missing records

```java
import com.aerospike.client.sdk.Record;

import com.aerospike.client.sdk.RecordResult;

import com.aerospike.client.sdk.RecordStream;

// ❌ Using exceptions for ordinary "not found" control flow

// Prefer treating an empty query stream (or Optional) as expected.

// ✅ Use Optional when missing is expected

RecordStream stream = session.query(users.id("user-1")).execute();

Optional<RecordResult> user = stream.getFirst();

if (user.isEmpty()) {

    // Create new user

}
```

## Next steps

Error Codes Reference

Complete list of error codes and their meanings.

[Error Codes →](https://aerospike.com/docs/develop/client/sdk/reference/error-codes)

Behaviors

Configure timeouts and retry behavior.

[Behaviors →](https://aerospike.com/docs/develop/client/sdk/concepts/behaviors)

Connect to Aerospike

Handle connection errors.

[Connect →](https://aerospike.com/docs/develop/client/sdk/connect)

Data Model

Understand keys, generations, and metadata.

[Data Model →](https://aerospike.com/docs/develop/client/sdk/concepts/data-model)