Skip to content

Error Handling

Exception Handling

General Information

When working with the Java client, you may encounter the following categories of errors:

Error Types
Tarantool-side Error Error Representation in Java Client Description
Exceptional situations occurring during Lua code execution on the Tarantool application server. Exceptions are thrown via box.error(...) or error(...) Errors are transmitted to the Java client at the IProto protocol level using a special package. In the Java client, such errors are wrapped in an exception of type BoxError

You can get this type of error in the Java client in the following cases:

  • Errors during use of TarantoolBoxSpace API
  • Explicit call of box.error(...) or error(...) during execution of stored procedure code via TarantoolClient#call(...)
  • Explicit call of box.error(...) or error(...) during execution of code via TarantoolClient#eval(...)
  • Other errors occurring during work with TarantoolBoxClient API, TarantoolClient#eval(...), TarantoolClient#call(...), described in file
Errors returned as structures within a multivalue tuple without throwing exceptions (similar to Go) on the Tarantool side The response from Tarantool is converted according to general deserialization rules This situation is not considered exceptional by the Java client. The response with an error object is converted to Java objects according to general deserialization rules. When working with TarantoolCrudClient, the tuple response is converted to an object of type CrudResponse
- Exceptions are generated on the Java client side These exceptions are generated by the Java client itself depending on various situations. See the full list of exceptions

Exceptional Situations on Tarantool Side

TarantoolBoxClient API

When using TarantoolBoxClient, you can get an exception of type BoxError in case of incorrect API usage.

Example 1: Re-inserting a record with the same identifier

Create a space named person:

person_space = box.schema.space.create('person', {
    if_not_exists = true,
    format = {
        { 'id', type = 'uuid' },
        { 'value', type = 'string', is_nullable = true }
    }
})
person_space:create_index('pk', { parts = { 'id' } })

Create an instance of TarantoolBoxClient and insert a record (the record should appear in Tarantool):

final TarantoolBoxClient boxClient = TarantoolFactory.box()
    .host("paste-host-name")
    .port(3302)
    .user("paste-username")
    .password("paste-username-password")
    .build();

final TarantoolBoxSpace space = boxClient.space("person");
final List<?> tuple = Arrays.asList(UUID.randomUUID(), "some_value");

final Tuple<List<?>> insertedTuple = space.insert(tuple);

Re-inserting a record will cause a CompletionException to be thrown with a reference (caused by) to BoxError:

final List<?> tupleWithNewValue = Arrays.asList(tuple.get(0), "some_value_2");

// stacktrace: 
//    java.util.concurrent.CompletionException: io.tarantool.core.exceptions.BoxError:
//    BoxError{code=3, message='Duplicate key exists in unique index "pk" in space "tt" with 
//    old tuple - [bdf657fc-5779-46d6-aea6-402e4a5eee38, "some_value"] and new tuple -
//    [bdf657fc-5779-46d6-aea6-402e4a5eee38, "some_value"]', 
//    stack=[BoxErrorStackItem{type='ClientError', line=1133, file='./src/box/memtx_tree.cc',
//    message='Duplicate key exists in unique index "pk" in space "tt" with old tuple - 
//    [bdf657fc-5779-46d6-aea6-402e4a5eee38, "some_value"] and new tuple -
//    [bdf657fc-5779-46d6-aea6-402e4a5eee38, "some_value"]', errno=0, code=3, details=null}]}
final Tuple<List<?>> insertedTuple = space.insert(tuple).join();
Example 2: Deleting a record with an incorrect key type

We will use the person space created in Example 1. Delete a record by passing an incorrect key (represented as a string):

final TarantoolBoxSpace space = boxClient.space("person");

final String uuidAsString = tuple.get(0).toString();

// stacktrace: 
//    io.tarantool.core.exceptions.BoxError: BoxError{code=18, message='Supplied key
//    type of part 0 does not match index part type: expected uuid', 
//    stack=[BoxErrorStackItem{type='ClientError', line=850, file='./src/box/key_def.h',
//    message='Supplied key type of part 0 does not match index part type: expected uuid', 
//    errno=0, code=18, details=null}]}
final Tuple<List<?>> deletedTuple = space.delete(uuidAsString).join();
Important

To learn more about the box module, refer to documentation

Exceptions on Tarantool Side During Call to TarantoolClient#call(...)

Create stored procedures that check the type of the passed parameter:

function check_parameter_with_error(param)
    if type(param) ~= "string" then
        error("Parameter must be a string")
    end
    return "Parameter is valid"
end

box.schema.func.create('check_parameter_with_error')
function check_parameter_with_box_error(param)
    if type(param) ~= "string" then
        box.error(box.error.PROC_LUA, "Parameter must be a string")
    end
    return "Parameter is valid"
end

box.schema.func.create('check_parameter_with_box_error')

Call the stored procedures from Java:

import java.util.Collections;
import java.util.List;
import java.util.UUID;

final List<String> stringParam = Collections.singletonList("some_value");

// OK 
// TarantoolResponse(data = [Parameter is valid], formats = {})
final TarantoolResponse<List<?>> result =
    boxClient.call("check_parameter_with_error", stringParam).join();

final List<UUID> param = Collections.singletonList(UUID.randomUUID());

// Stacktrace:
//    java.util.concurrent.CompletionException: io.tarantool.core.exceptions.BoxError: 
//    BoxError{code=32, message='Parameter must be a string', 
//    stack=[BoxErrorStackItem{type='ClientError', line=3, 
//    file='[string "function check_parameter_with_box_error(param..."]', 
//    message='Parameter must be a string', errno=0, code=32, details=null}]}
final TarantoolResponse<List<?>> exceptionallyResult =
    boxClient.call("check_parameter_with_error", param).join();
import java.util.Collections;
import java.util.List;
import java.util.UUID;

final List<String> stringParam = Collections.singletonList("some_value");

// OK
// TarantoolResponse(data = [Parameter is valid], formats = {})
final TarantoolResponse<List<?>> result =
    boxClient.call("check_parameter_with_box_error", stringParam).join();

final List<UUID> param = Collections.singletonList(UUID.randomUUID());

// Stacktrace: 
//    io.tarantool.core.exceptions.BoxError: BoxError{code=32, message='Parameter must be a 
//    string', stack=[BoxErrorStackItem{type='ClientError', line=3, 
//    file='[string "function check_parameter_with_box_error(param..."]', 
//    message='Parameter must be a string', errno=0, code=32, details=null}]}
final TarantoolResponse<List<?>> exceptionallyResult =
    boxClient.call("check_parameter_with_box_error", param).join();

Exceptions on Tarantool Side During Call to TarantoolClient#eval(...)

Execute the following Lua code through the Java client:

if not box.space[space_name] then 
    box.error(box.error.NO_SUCH_SPACE, string.format("Space does not exist: %s", space_name)) 
end
return string.format("Space '%s' exists", space_name)
final String luaCode = "if not box.space[space_name] then "
    + "box.error(box.error.NO_SUCH_SPACE, string.format(\"Space does not exist: %s\", space_name)) "
    + "end return string.format(\"Space '%s' exists\", space_name)";

// OK
// TarantoolResponse(data = [Space 'person' exists], formats = {})
final TarantoolResponse<List<?>> result = client.eval(luaCode, "person").join();

// Stacktrace:
//    java.util.concurrent.CompletionException: io.tarantool.core.exceptions.BoxError: 
//    BoxError{code=36, message='Space 'Space does not exist: unknown_space' does not exist', 
//    stack=[BoxErrorStackItem{type='ClientError', line=1, file='eval', message='Space 'Space does 
//    not exist: unknown_space' does not exist', errno=0, code=36, details=null}]}
final TarantoolResponse<List<?>> exceptionallyResult = client.eval(luaCode, "person").join();

Go-like Errors

TarantoolCrudClient

TarantoolCrudClient is a wrapper around the crud module API. Most API methods return a result as a tuple (res, err), where err is an error object. In the Java client the result of a crud API call is wrapped in an object of type CrudResponse. From the perspective of the IProto protocol, returning a tuple with an error object is not an exceptional situation, but in case of an error (err != nil), TarantoolCrudClient itself throws an exception of type CrudError (similar to TarantoolBoxClient, which throws BoxError) to signal that an error object was returned.

As an example, create a sharded space person on a vshard cluster with the following format:

format = {
    { 'id', type = 'uuid' },
    { 'value', type = 'string', is_nullable = true },
    { 'bucket_id', 'unsigned' },
}

Add records to the cluster:

import java.util.Arrays;
import java.util.UUID;

final TarantoolCrudClient crudClient = TarantoolFactory.crud()
    .host("router-hostname")
    .port(3301)
    .user("username")
    .password("username-password")
    .build();

final TarantoolCrudSpace space = crudClient.space("person");

final List<?> tuple = Arrays.asList(UUID.randomUUID(), "some_value");

// OK
//    Tuple(
//      formatId = 55, 
//      data = [ca95b6c7-134d-476a-8545-1ce038cb3b18, some_value, 1208], 
//      format = [
//        Field{
//          name='id', 
//          type='uuid', 
//          isNullable=null, 
//          collation='null', 
//          constraint=null, 
//          foreignKey=null
//        }, 
//        Field{
//          name='value', 
//          type='string', 
//          isNullable=null, 
//          collation='null', 
//          constraint=null, 
//          foreignKey=null
//        }, 
//      Field{
//        name='bucket_id', 
//        type='unsigned', 
//        isNullable=null, 
//        collation='null', 
//        constraint=null, 
//        foreignKey=null
//      }
//    ]
//   )
final Tuple<List<?>> insertedTuple = space.insert(tuple).join();

final List<?> tupleWithSameId = Arrays.asList(tuple.get(0), "second_value");

// Stacktrace:
//    java.util.concurrent.CompletionException: io.tarantool.mapping.crud.CrudException: 
//    InsertError: Failed to insert: Duplicate key exists in unique index "pk" in space "person" 
//    with old tuple - [ca95b6c7-134d-476a-8545-1ce038cb3b18, "some_value", 1208] and 
//    new tuple - [ca95b6c7-134d-476a-8545-1ce038cb3b18, "second_value", 1208]
final Tuple<List<?>> exceptionallyTuple = space.insert(tupleWithSameId).join();

Returning Go-like Errors Through Stored Procedures

Tarantool allows returning Go-like tuples in the return value of stored procedures. In this case, the user must take care of proper conversion of lua and Java types, based on the rules of conversion.

Consider the following procedure:

function multiply_by_100(input)
    if type(input) ~= "number" then
        return nil, "Input is not a number"
    end
    local result = input * 100
    return result, nil
end

box.schema.func.create('multiply_by_100')
final TarantoolClient client = TarantoolFactory.box()
    .host("hostname")
    .port(3301)
    .user("username")
    .password("username-password")
    .build();

// OK
// TarantoolResponse(data = [10000, null], formats = {})
final TarantoolResponse<List<?>> result = client.call("multiply_by_100", Arrays.asList(100)).join();

// OK
// TarantoolResponse(data = [null, Input is not a number], formats = {})
final TarantoolResponse<List<?>> resultWithError =
    client.call("multiply_by_100", Arrays.asList("100")).join();

Various lua types can be returned as values, for example, arrays:

function prime_factors(input)
    if type(input) ~= "number" or input < 2 then
        return nil, "Input is not a valid number (must be an integer >= 2)"
    end

    local factors = {}
    local divisor = 2

    while input >= divisor do
        while (input % divisor) == 0 do
            table.insert(factors, divisor)
            input = input / divisor
        end
        divisor = divisor + 1
    end

    return factors, nil
end

box.schema.func.create('prime_factors')
import java.util.Arrays;

// OK
// TarantoolResponse(data = [[2, 2, 5, 5], null], formats = {})
final TarantoolResponse<List<?>> result = client.call("prime_factors", Arrays.asList(100)).join();

    // OK
// TarantoolResponse(data = [null, Input is not a valid number (must be an integer >= 2)], 
// formats = {})
    final TarantoolResponse<List<?>> result =
        client.call("prime_factors", Arrays.asList(100)).join();

Exceptions Generated by the Java Client

Exception Types
Exception Type Description
BadGreetingException Thrown in case of failed greeting with the Tarantool node
BalancerException Base exception thrown when the balancer is not working properly
NoAvailableClientsException Subclass of BalancerException. Thrown when the balancer cannot find any "alive" connection in the connection pool
SchemaFetchingException Subclass of ClientException. Thrown when an error occurs during loading of space schemas
ConnectionException Base exception for exceptions related to low-level connections (Connection interface)
ConnectionClosedException Subclass of ConnectionException. Thrown when a connection is closed abnormally by the Tarantool node or by the Java client, when the connection to the node was initiated but was interrupted before the actual connection was established
CrudException Base class of exceptions when working with TarantoolCrudSpace API. Thrown when there is an error working with TarantoolCrudSpace API (for example, inserting a record with the same key)
ServerException Base class of exceptions for server errors, such as outdated protocol version
JacksonMappingException Base class of exceptions for serialization and deserialization type errors
NoSchemaException Subclass of ClientException. Thrown when calling TarantoolBoxClient#space(...) when a space with the passed identifier or name does not exist
ShutdownException Subclass of ClientException. Class of exceptions for clients, such as graceful shutdown errors
PoolException Base class of exceptions occurring during connection pool operation
PoolClosedException Subclass of PoolException. Thrown when attempting to execute requests on a previously closed Java client
TimeoutException Thrown when a client action exceeds the specified time (connection, greeting, request timeout)