Skip to content

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).

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 field

Common 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)
}

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 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
}

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 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 ...
}

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());
}

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());
}

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());
}

Actionable error messages

Every Developer SDK exception includes:

PropertyDescription
messageHuman-readable description of what went wrong
errorCode / result_codeCode for programmatic handling (getResultCode() in Java; result_code on AerospikeError in Python)
contextAdditional 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()
));
}

Retry strategies

The Aerospike Developer SDKs feature automatic retry functionality. See the Behaviors topic for more information.

Retryable vs non-retryable errors

JavaPythonRetryableReason
AerospikeException.TimeoutTimeoutError (from aerospike_sdk)✅ YesTransient network issue
AerospikeException.ConnectionConnectionError (from aerospike_sdk)✅ YesNode may recover
AerospikeException.RecordExistsExceptionAerospikeError with result_code == KEY_EXISTS_ERROR❌ NoLogic error, needs different approach
AerospikeException.RecordNotFoundExceptionAerospikeError with result_code == KEY_NOT_FOUND_ERROR❌ NoRecord doesn’t exist
AerospikeException.GenerationExceptionGenerationError⚠️ MaybeRe-read and recalculate first
AuthenticationExceptionAuthenticationError❌ NoWrong credentials
Invalid AEL / where(...) textSame (raised as AerospikeError)❌ NoFix the query syntax

Mapping to classic client exceptions

If you’re migrating from the classic Aerospike client:

Classic ClientDeveloper SDK (Java)Notes
AerospikeExceptionAerospikeExceptionBase class
AerospikeException.TimeoutAerospikeException.TimeoutSame semantic class
AerospikeException.ConnectionAerospikeException.ConnectionSame semantic class
Result code KEY_EXISTS_ERRORAerospikeException.RecordExistsExceptionTyped exception
Result code KEY_NOT_FOUND_ERRORAerospikeException.RecordNotFoundExceptionTyped exception
Result code GENERATION_ERRORAerospikeException.GenerationExceptionTyped exception

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

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

// ❌ 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

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 →

Connect to Aerospike

Handle connection errors.

Connect →

Data Model

Understand keys, generations, and metadata.

Data Model →

Feedback

Was this page helpful?

What type of feedback are you giving?

What would you like us to know?

+Capture screenshot

Can we reach out to you?