Error handling
The three execution modes
Every builder’s .execute() method supports three modes that control how per-record errors are surfaced:
| Mode | Java | Python | Behavior | Best for |
|---|---|---|---|---|
| Default | execute() | await … .execute() | Single-key: throws immediately. Batch: errors embedded in stream | Simple cases |
| In-stream errors | execute(ErrorStrategy.IN_STREAM) | await … .execute(on_error=ErrorStrategy.IN_STREAM) | All errors embedded as RecordResult entries in the stream | Uniform handling of single + batch |
| Error callback | execute(errorHandler) | await … .execute(on_error=<callable>) | Errors dispatched to callback, excluded from stream | Logging, fire-and-forget |
Java exposes the same three modes via overloads of execute() and
executeAsync(). The Python SDK uses await … .execute(on_error=…) as the
entry point for the async surface (aerospike_sdk.aio.*); the synchronous
surface (aerospike_sdk.sync.*) wraps the same builders behind blocking
.execute(on_error=…) calls. Either way there is no separate executeAsync()
— choosing async vs sync is a matter of which session/client class you import.
Mode 1: Default (execute())
Single-key operations throw on failure:
try { session.update(users.id("no-such-key")) .bin("name").setTo("Alice") .execute();} catch (AerospikeException e) { System.out.println("Failed: " + e.getResultCode()); // 2 = KEY_NOT_FOUND_ERROR}from aerospike_sdk import AerospikeError
try: await ( session.update(users.id("no-such-key")) .bin("name").set_to("Alice") .execute() )except AerospikeError as e: print(f"Failed: {e.result_code}") # 2 = KEY_NOT_FOUND_ERRORBatch operations embed errors in the stream — they do not throw:
RecordStream stream = session .update(users.id("exists")) .bin("count").add(1) .update(users.id("no-such-key")) .bin("count").add(1) .execute();
stream.forEach(result -> { if (result.isOk()) { System.out.println(result.key().userKey + ": success"); } else { System.out.println(result.key().userKey + ": " + result.message()); }});stream = await ( session.update(users.id("exists")) .bin("count").add(1) .update(users.id("no-such-key")) .bin("count").add(1) .execute())try: async for result in stream: if result.is_ok: print(f"{result.key.value}: success") else: print(f"{result.key.value}: {result.exception or str(result.result_code)}")finally: stream.close()Mode 2: ErrorStrategy.IN_STREAM
Forces all errors (including single-key) into the stream. Useful when you want uniform handling regardless of key count:
RecordStream stream = session.update(users.id("no-such-key")) .bin("name").setTo("Alice") .execute(ErrorStrategy.IN_STREAM);
stream.forEach(result -> { if (!result.isOk()) { System.out.println("Error: " + result.resultCode() + " - " + result.message()); }});from aerospike_sdk import ErrorStrategy
stream = await ( session.update(users.id("no-such-key")) .bin("name").set_to("Alice") .execute(on_error=ErrorStrategy.IN_STREAM))try: async for result in stream: if not result.is_ok: print(f"Error: {result.result_code} - {result.exception or str(result.result_code)}")finally: stream.close()Mode 3: ErrorHandler callback
Errors are dispatched to the handler and excluded from the stream. The stream contains only successful results:
RecordStream stream = session .upsert(users.id("u1")).bin("name").setTo("Alice") .upsert(users.id("u2")).bin("name").setTo("Bob") .execute((key, index, exception) -> System.err.println("Failed key " + key.userKey + " at index " + index + ": " + exception.getMessage()) );
// stream contains only successful resultsstream.forEach(result -> { System.out.println(result.key().userKey + ": OK");});def handle_error(key, index, ex): print(f"Failed key {key.value} at index {index}: {ex}")
stream = await ( session.upsert(users.id("u1")).bin("name").set_to("Alice") .upsert(users.id("u2")).bin("name").set_to("Bob") .execute(on_error=handle_error))# stream contains only successful resultstry: async for result in stream: print(f"{result.key.value}: OK")finally: stream.close()Inspecting RecordResult
Every result in the stream is a RecordResult. Key members:
| Java | Python | Returns | Throws? |
|---|---|---|---|
isOk() | is_ok (attribute) | true if result_code == OK | No |
resultCode() | result_code (attribute) | Integer result code | No |
message() | (use exception / result_code) | Human-readable error message (or null/None) | No |
orThrow() | or_raise() | self if OK, throws otherwise | Yes |
recordOrThrow() | record_or_raise() | The Record if OK, throws otherwise | Yes |
recordOrNull() | record (attribute, may be None) | The Record or null/None — does not throw | No |
asBoolean() | as_bool() | true if OK, false if KEY_NOT_FOUND, throws otherwise | Conditionally |
key() | key (attribute; user value via key.value) | The Key for this operation | No |
index() | index (attribute) | Position in the batch (0-based) | No |
inDoubt() | in_doubt (attribute) | Whether the write may have succeeded on the server | No |
exception() | exception (attribute) | The AerospikeException / AerospikeError if available, or None | No |
Using failures() to isolate errors
RecordStream.failures() consumes the entire source stream and returns only
the failed results. In Java this is a new RecordStream; in Python it is an
awaitable that resolves to list[RecordResult].
RecordStream stream = session .update(users.id("u1")).bin("count").add(1) .update(users.id("u2")).bin("count").add(1) .update(users.id("missing")).bin("count").add(1) .execute(ErrorStrategy.IN_STREAM);
RecordStream errors = stream.failures();errors.forEach(failure -> System.err.println("Failed: " + failure.key().userKey + " → " + failure.message()));from aerospike_sdk import ErrorStrategy
stream = await ( session.update(users.id("u1")).bin("count").add(1) .update(users.id("u2")).bin("count").add(1) .update(users.id("missing")).bin("count").add(1) .execute(on_error=ErrorStrategy.IN_STREAM))errors = await stream.failures()for f in errors: print(f"Failed: {f.key.value} → {f.result_code}")stream.close()Note:
failures()is a terminal-style helper — it drains and exhausts the source stream and returns the failures fully in memory (a newRecordStreamin Java; alist[RecordResult]in Python). In Python it does not callstream.close()for you; if you need to release client/server resources promptly, callstream.close()(or wrap the work intry/finally) after collecting the failures.
Existence checks
The asBoolean() / as_bool() method translates OK → true, KEY_NOT_FOUND → false, any other error → throws:
boolean exists = session.exists(users.id("user-1")) .execute() .getFirstBoolean() .orElse(false);stream = await session.exists(users.id("user-1")).execute()try: first = await stream.first() exists = first.as_bool() if first else Falsefinally: stream.close()Decision guide
| Scenario | Recommended mode |
|---|---|
| Simple single-key read/write | execute() + try/catch |
| Batch where all failures are fatal | execute() + iterate and check isOk() / is_ok |
| Batch where you need consistent handling | execute(ErrorStrategy.IN_STREAM) (Java) / execute(on_error=ErrorStrategy.IN_STREAM) (Python) |
| Batch where failures should be logged but not block | execute(errorHandler) (Java) / execute(on_error=<callable>) (Python) |
| Need to collect all failures for retry | IN_STREAM mode + .failures() |