Error handling
For the complete documentation index see: llms.txt
All documentation pages available in markdown.
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. The tree below documents the Java SDK hierarchy.
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)}📖 API reference:
Session.insert(DataSet)|Session.upsert(DataSet)|OperationObjectBuilder.bins(...)|IdValuesBuilder.id(...)|IdValuesRowBuilder.values(...)|ChainableQueryBuilder.execute()|AerospikeException
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)📖 API reference:
DataSet.id()|Session.upsert()|Session.insert()|WriteSegmentBuilder.put()|WriteSegmentBuilder.execute()
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}📖 API reference:
DataSet.id(...)|Session.query(Key)|ChainableQueryBuilder.execute()|RecordStream.getFirst()|RecordResult.isOk()|AerospikeException
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()📖 API reference:
DataSet.id()|Session.query()|RecordResult.record_or_raise()|RecordStream.first()|RecordStream.close()|QueryBuilder.execute()
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 ...}📖 API reference:
DataSet.id(...)|Session.update(DataSet)|Session.query(Key)|ChainableOperationBuilder.bin(...)|ChainableQueryBuilder.bin(...)|ChainableQueryBuilder.execute()|RecordStream.getFirstRecord()
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 ...📖 API reference:
DataSet.id()|Session.query()|Session.update()|QueryBuilder.bin()|WriteSegmentBuilder.set_to()|WriteSegmentBuilder.bin()|WriteSegmentBuilder.ensure_generation_is()|RecordResult.record_or_raise()|RecordStream.first_or_raise()|RecordStream.close()|QueryBuilder.execute()|WriteSegmentBuilder.execute()
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());}📖 API reference:
Session.query(DataSet)|ChainableQueryBuilder.where(...)|ChainableQueryBuilder.execute()|RecordStream.forEach(...)|AerospikeException
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}")📖 API reference:
Session.query()|QueryBuilder.where()|RecordStream.close()|QueryBuilder.execute()
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());}📖 API reference:
ClusterDefinition(String,int)|ClusterDefinition.connect()|AerospikeException
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))📖 API reference:
Exceptions
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());}📖 API reference:
Session.query(DataSet)|ChainableQueryBuilder.where(...)|ChainableQueryBuilder.execute()|RecordStream.close()
try: stream = await session.query(users).where("$.age > > 21").execute() stream.close()except Exception as e: print(f"Invalid filter expression: {e}")📖 API reference:
Session.query()|QueryBuilder.where()|RecordStream.close()|QueryBuilder.execute()
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() ));}📖 API reference:
Session.insert(DataSet)|OperationObjectBuilder.bins(...)|IdValuesBuilder.id(...)|IdValuesRowBuilder.values(...)|ChainableQueryBuilder.execute()|AerospikeException
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), })📖 API reference:
DataSet.id()|Session.insert()|WriteSegmentBuilder.put()|WriteSegmentBuilder.execute()
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}📖 API reference:
DataSet.id(...)|Session.query(Key)|ChainableQueryBuilder.execute()|RecordStream.getFirst()|AerospikeException
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;}📖 API reference:
Session.insert(DataSet)|OperationObjectBuilder.bins(...)|IdValuesBuilder.id(...)|IdValuesRowBuilder.values(...)|ChainableQueryBuilder.execute()|AerospikeException
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}📖 API reference:
DataSet.id(...)|Session.query(Key)|ChainableQueryBuilder.execute()|RecordStream.getFirst()
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.