Skip to content

Debug AEL expressions

For the complete documentation index see: llms.txt

All documentation pages available in markdown.

Task guide: move from a failed AEL string or a silent filter mismatch to a concrete cause using server error details.

Applies to

  • Aerospike Developer SDKs
  • Aerospike Database 8.2.0.0 and later (server-side AEL compilation and structured error details)

Audience

Developers authoring AEL filter or operation-expression text (Intermediate).

Prerequisites

Outcome

You can read a parse or build failure in your own AEL source, use aelOffset and aelSpan to highlight the error, and interpret why a single-record filter rejected a record without assuming a trace is always present.

When to use this page

SymptomStart hereAlso see
Malformed AEL, wrong types at compile timeParse and build errorsAEL reference
Need the exact characters that failedSource coordinates in the trace
Filter returned nothing / FILTERED_OUTFilter-decision explainerAuthor AEL expressions
Which verbosity to set, byte budget, operator capServer error details

Neither SDK parses AEL locally. Every .where(...), selectFrom(...) / select_from(...), and write-side *From(...) call sends your string to the server. Diagnostics come back on the failure response when you opt in.

Enable diagnostics

Set errorDetailVerbosity on the session Behavior before you run the failing command:

GoalVerbosityWhat you get for AEL
Contextual build message (includes line/column for parse errors)MESSAGE (2)server_message / exception message text
Build trace or filter explainerEXPRESSION_TRACE (3)Structured ExpressionTrace (Java) or exp_trace (Python) when the server attaches one
import com.aerospike.client.sdk.ErrorDetailVerbosity;
import com.aerospike.client.sdk.policy.Behavior;
Behavior withDetails = Behavior.DEFAULT.deriveWithChanges("debugAel", builder -> builder
.on(Behavior.Selectors.all(), ops -> ops
.errorDetailVerbosity(ErrorDetailVerbosity.EXPRESSION_TRACE)
)
);
Session session = cluster.createSession(withDetails);

📖 API reference: ErrorDetailVerbosity | Behavior | Behavior.deriveWithChanges()

Use verbosity 3 only while debugging. Keep production code at 0 or 1 unless you have a specific reason to run higher. See Choosing a verbosity.

Parse and build errors

A malformed or ill-typed AEL string fails at build time on the server. The client surfaces PARAMETER_ERROR (4). That is a build-time failure, not a filter mismatch.

The server’s message names which slot held the expression, then folds in the AEL compiler diagnostic. For a parse error, line and column appear inside that message string. Read the message text. Do not look for them in the structured trace.

SlotTypical message prefixSDK entry point
Filter on a single-key commandinvalid filter expression in request.where(...) on .query(key) or .update(key)
Filter in a batch requestinvalid filter expression (batch context in the full message)Per-key .where(...) in a multi-key .query(...) chain
Filter on a set queryinvalid filter expression in query.where(...) on .query(dataset) (fails before the query runs)
Operation expressioninvalid expression in operation request.selectFrom(...) / .select_from(...), upsertFrom, insertFrom, and updateFrom

For a parse failure, the folded diagnostic after the prefix usually includes line and column, for example unexpected end of expression at line 1 col 15 for a trailing and with no right-hand operand.

Example: filter parse error

Trailing and with no right-hand operand is a parse failure:

$.score:INT > 30 and
import com.aerospike.client.sdk.AerospikeException;
import com.aerospike.client.sdk.ResultCode;
String ael = "$.score:INT > 30 and";
try {
session.query(users.id("user-1")).where(ael).execute();
}
catch (AerospikeException e) {
if (e.getResultCode() == ResultCode.PARAMETER_ERROR) {
// e.getMessage() / getBaseMessage(): slot prefix + AEL diagnostic (line/column for parse errors)
System.out.println(e.getMessage());
}
}

Example: operation-expression type error

Integer bin plus string literal fails at compile time:

$.qty:INT + 'x'
try {
session.query(orders.id("order-1"))
.bin("bad")
.selectFrom("$.qty:INT + 'x'")
.execute();
}
catch (AerospikeException e) {
if (e.getResultCode() == ResultCode.PARAMETER_ERROR) {
System.out.println(e.getMessage()); // expect "invalid expression in operation request" prefix
}
}

Example: set-query filter build failure

A malformed filter on .query(dataset) fails while the server plans the query, before any records are read. The message prefix is invalid filter expression in query:

try {
session.query(users)
.where("$.age > 30 and")
.execute();
}
catch (AerospikeException e) {
if (e.getResultCode() == ResultCode.PARAMETER_ERROR) {
System.out.println(e.getMessage());
}
}

Example: batch filter build failure

In a multi-key batch read, each key can carry its own filter. A bad AEL string on one key returns PARAMETER_ERROR on that row only. Siblings with valid filters still succeed. Inspect RecordResult / stream rows rather than expecting a top-level exception:

import com.aerospike.client.sdk.ExpressionTrace;
import com.aerospike.client.sdk.RecordStream;
import com.aerospike.client.sdk.ResultCode;
RecordStream stream = session
.query(users.id("u1")).where("$.score == 1")
.query(users.id("u2")).where("$.score:INT > 30 and")
.execute();
stream.forEach(result -> {
if (!result.isOk() && result.getResultCode() == ResultCode.PARAMETER_ERROR) {
System.out.println(result.getKey().userKey + ": " + result.getMessage());
ExpressionTrace trace = result.getExpressionTrace();
if (trace != null) {
System.out.println(trace);
}
}
});
stream.close();

Source coordinates in the trace

At verbosity EXPRESSION_TRACE, a build-phase failure can include a structured trace with a source-language marker and coordinates into your AEL string:

  • lang: LANG_AEL for AEL-authored expressions (msgpack Exp builders use LANG_MSGPACK).
  • aelOffset / aelSpan (Java: getAelOffset(), getAelSpan()): character offset and byte width of the offending region in the AEL source text you passed to the SDK.
  • snippet: An AEL fragment and location marker showing the part of the AEL which caused the failure

Two coordinate spaces

FieldIndexes
aelOffset + aelSpanYour AEL source text: character offset plus UTF-8 byte span of the offending region
byteOffsetThe compiled msgpack expression payload on the wire (not useful for AEL authors)

Line and column are not structured trace fields. Wire keys for ael_line and ael_col are reserved and are not emitted. For parse failures, read line and column from the message at verbosity 2 (previous section). An expected[] list of valid productions does not ship.

Highlight the offending region

For a simple way to show the part of the AEL which caused the failure including a marker of the error in the fragment, using snippet. Fro more complex processing, keep the exact AEL string in a variable so you can slice it when a trace is present. aelOffset is a character index. aelSpan is a byte width in UTF-8. Do not pass start + span to substring() or string slicing when using mult-byte characters.

import java.nio.charset.StandardCharsets;
import com.aerospike.client.sdk.AerospikeException;
import com.aerospike.client.sdk.ExpressionTrace;
String ael = "$.score:INT > 30 and";
try {
session.query(users.id("user-1")).where(ael).execute();
}
catch (AerospikeException e) {
ExpressionTrace trace = e.getExpressionTrace();
if (trace != null
&& trace.getLang() == ExpressionTrace.LANG_AEL
&& trace.getSnippet()!= null) {
System.out.println("Near: " + trace.getSnippet());
}
else {
System.out.println(e.getMessage());
}
}

Always branch on trace != null (or equivalent). See When the trace is missing.

Filter-decision explainer

When a filter evaluates to false (or faults, or hits absent data), a query or read normally omits the record with no error. To surface the decision, call .failOnFilteredOut() / .fail_on_filtered_out() and handle FILTERED_OUT (27).

At verbosity EXPRESSION_TRACE, an eval-phase trace can explain the decision:

outcomeMeaning
OUTCOME_FALSEFilter ran. The record did not match.
OUTCOME_ABSENTA referenced bin or key was absent
OUTCOME_FAULTEvaluation fault (type mismatch or divide-by-zero)

For a decisive comparison, operands carries the two values that decided the outcome (subject to clipping and truncation). See Expression traces.

Example: record rejected by a filter

Record has score = 1. Filter is $.score == 99:

import com.aerospike.client.sdk.AerospikeException;
import com.aerospike.client.sdk.ExpressionTrace;
try {
session.query(users.id("user-1"))
.where("$.score == 99")
.failOnFilteredOut()
.execute();
}
catch (AerospikeException.FilteredException e) {
ExpressionTrace trace = e.getExpressionTrace();
if (trace != null) {
System.out.println("phase=" + trace.getPhase()
+ " outcome=" + trace.getOutcome());
if (trace.getOutcome() == ExpressionTrace.OUTCOME_FALSE) {
System.out.println(" Operands:" + java.util.Arrays.toString(trace.getOperands()));
}
}
}

📖 API reference: AerospikeException.FilteredException | ExpressionTrace | ChainableQueryBuilder.failOnFilteredOut()

Limits on the explainer

  • Read permission: Operand values are bin contents. A principal without read access does not receive filter explanations.
  • Query path: Queries filter many records per request. You get build-time traces when a query’s filter fails to compile. Per-record filter explainers are not returned on the query path (Server error details: queries return details only when the query fails to start). Use a single-key read (or batch key operation) with .failOnFilteredOut() / .fail_on_filtered_out() to debug why one record did not match.

When the trace is missing

A null trace is normal. Four independent mechanisms can remove it:

  1. Verbosity below 3: only messages (or less) are returned.
  2. Byte budget: the server drops whole trace parts (operands first, then snippet) when detail would exceed about 1 KB per record.
  3. Operator cap: error-details-max-verbosity limits the cluster regardless of client request.
  4. Read permission: no explainer for write-only principals.

Write examples to degrade gracefully: print the message when getExpressionTrace() / exp_trace is absent.

Verify

  1. Reproduce the failure with errorDetailVerbosity set to MESSAGE or EXPRESSION_TRACE.
  2. For build failures, confirm PARAMETER_ERROR and read the slot prefix in the message.
  3. For “why not this record?”, use single-key .failOnFilteredOut() and confirm FILTERED_OUT with an eval-phase trace when permitted.
  4. If a trace is present with LANG_AEL, slice your source string on UTF-8 bytes using aelOffset (char index) and aelSpan (byte width), and confirm the highlighted region matches the mistake.
  5. Read the record directly to confirm operand hints. Do not treat clipped operand strings as authoritative bin values.

Next steps

AEL reference

Grammar, paths, and type rules for valid AEL text.

AEL reference →

Author AEL expressions

Pass AEL to filters and operation expressions on each command type.

Author AEL expressions →

Server error details

Verbosity levels, trace shape, truncation, and operator caps.

Server error details →

SDK error handling

Execution modes, RecordResult, and batch failure isolation.

SDK error handling →