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:
- Actionable messages — Every error tells you what went wrong and how to fix it
- Typed exceptions — Catch specific error types, not generic exceptions
- Recovery hints — Errors include suggestions for resolution
- 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).
Exception hierarchy
All Developer SDK exceptions inherit from a base exception:
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 fieldCommon exceptions
RecordExistsException
Thrown when attempting to create a record that already exists.
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)}from aerospike_sdk import AerospikeErrorfrom 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.
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 rowRecordStream stream = session.query(users.id("nonexistent")).execute();if (stream.getFirst().isEmpty()) { System.out.println("Record not found");}
// Alternative: Optional when presentRecordStream 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}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.
import com.aerospike.client.sdk.Record;import com.aerospike.client.sdk.RecordResult;import com.aerospike.client.sdk.RecordStream;
// Read the recordRecord 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 ...}from aerospike_sdk import GenerationError
# Read the recordstream = 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.
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());}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.
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());}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).
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());}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) |
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() ));}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 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 |
Python SDK equivalents:
- Base class
AerospikeError. - Timeouts and connectivity map to
TimeoutErrorandConnectionErrorfromaerospike_sdk. - Generation mismatches use
GenerationError. - Result codes such as
KEY_EXISTS_ERRORandKEY_NOT_FOUND_ERRORsurface asAerospikeErrorwithresult_codeset. Compare againstaerospike_async.exceptions.ResultCodewhen you need to branch.
Error handling best practices
1. Catch specific exceptions
import com.aerospike.client.sdk.RecordStream;
// ❌ Don't catch generic Exceptiontry { RecordStream s = session.query(users.id("user-1")).execute(); s.getFirst().ifPresent(r -> { /* ... */ });} catch (Exception e) { // Lost type information}
// ✅ Do catch specific typestry { 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
// ❌ Silent failure hides bugstry { session.insert(users) .bins("name") .id("user-1").values("Alice") .execute();} catch (Exception e) { // Ignore}
// ✅ Log or handle appropriatelytry { 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
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 expectedRecordStream 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.
Behaviors
Configure timeouts and retry behavior.
Connect to Aerospike
Handle connection errors.
Data Model
Understand keys, generations, and metadata.