import ballerina/io;const INVALID_ACC_TYPE = "InvalidAccountType";
type InvalidAccountTypeErrorData record {
    string message?;
    error cause?;
    string accountType;
};
type InvalidAccountTypeError error<INVALID_ACC_TYPE, InvalidAccountTypeErrorData>;function getTypeId(string accountType) returns int | InvalidAccountTypeError {
    match accountType {
        "checking" => { return 1; }
        "savings" => { return 2; }
    }
    InvalidAccountTypeError e = InvalidAccountTypeError(accountType = accountType);
    return e;
}type AccountNotFoundErrorData record {
    string message?;
    error cause?;
    int accountID;
};const INVALID_ACCOUNT_ID = "InvalidAccountID";
const ACCOUNT_NOT_FOUND = "AccountNotFound";
type AccountNotFoundError error<ACCOUNT_NOT_FOUND | INVALID_ACCOUNT_ID, AccountNotFoundErrorData>;function getAccountBalance(int accountID) returns int|AccountNotFoundError {
    if (accountID < 0) {
        AccountNotFoundError accountNotFoundError =
                                            error(INVALID_ACCOUNT_ID, accountID = accountID);
        return accountNotFoundError;
    } else if (accountID > 100) {
        AccountNotFoundError accountNotFoundError =
                                            error(ACCOUNT_NOT_FOUND, accountID = accountID);
        return accountNotFoundError;
    }
    return 600;
}
type InquiryFailedErrorData record {|
    string message;
    error cause;
    int accountID;
|};type AccountInquiryFailed error<string, InquiryFailedErrorData>;function transferToAccount(int fromAccountId, int toAccountId, int amount) returns int|AccountInquiryFailed {
    var balance = getAccountBalance(fromAccountId);
    if (balance is error) {
        AccountInquiryFailed e = error("AccountInquiryFailed", message = balance.reason(), cause = balance, accountID = fromAccountId);
        return e;
    } else {
    }    return 0;
}public function main() {
    int|InvalidAccountTypeError result = getTypeId("Joined");
    if (result is int) {
        io:println("Account type ID: ", result);
    } else {
        io:println("Error: ", result.reason(),
                   ", Account type: ", result.detail().accountType);
    }    var result2 = getAccountBalance(-1);
    if (result2 is int) {
        io:println("Account Balance: ", result2);
    } else {
        io:println("Error: ", result2.reason(),
                    ", Account ID: ", result2.detail().accountID);
    }    var result3 = transferToAccount(-1, 90, 1000);
    if (result3 is int) {
        io:println("Transfer success: ", result3);
    } else {
        io:println("Error: ", result3.reason(),
                    ", Message: ", result3.detail().message,
                    ", Cause: ", result3.detail().cause);
    }
}

User-defined Error Types

Ballerina allows defining custom error types to match the errors being modeled. User defined error types in Ballerina should have a reason type which is a subtype of string, and a detail type which is a subtype of record {| string message?; error cause?; (anydata|error)...; |}.

The nature of the default error detail record is that each field is optional and can contain additional anydata|error fields as required.

import ballerina/io;
const INVALID_ACC_TYPE = "InvalidAccountType";
type InvalidAccountTypeErrorData record {
    string message?;
    error cause?;
    string accountType;
};

Define a record to represent the error details. This record can have fields of anydata|error types and should be a subtype of the built-in error’s detail type.

type InvalidAccountTypeError error<INVALID_ACC_TYPE, InvalidAccountTypeErrorData>;

User-defined error with a constant reason.

function getTypeId(string accountType) returns int | InvalidAccountTypeError {
    match accountType {
        "checking" => { return 1; }
        "savings" => { return 2; }
    }
    InvalidAccountTypeError e = InvalidAccountTypeError(accountType = accountType);
    return e;
}

When a constant reason is used in the error definition the error type name can be used as the error constructor, and the error details can be provided as named arguments, without specifying the reason.

type AccountNotFoundErrorData record {
    string message?;
    error cause?;
    int accountID;
};
const INVALID_ACCOUNT_ID = "InvalidAccountID";
const ACCOUNT_NOT_FOUND = "AccountNotFound";
type AccountNotFoundError error<ACCOUNT_NOT_FOUND | INVALID_ACCOUNT_ID, AccountNotFoundErrorData>;

Define an error type where error reason must be either ACCOUNT_NOT_FOUND or INVALID_ACCOUNT_ID.

function getAccountBalance(int accountID) returns int|AccountNotFoundError {
    if (accountID < 0) {
        AccountNotFoundError accountNotFoundError =
                                            error(INVALID_ACCOUNT_ID, accountID = accountID);
        return accountNotFoundError;
    } else if (accountID > 100) {

Return an error with “InvalidAccountID” as the reason if the accountID is less than zero. The default error constructor can be used to construct the error value.

        AccountNotFoundError accountNotFoundError =
                                            error(ACCOUNT_NOT_FOUND, accountID = accountID);
        return accountNotFoundError;
    }

Return an error with “AccountNotFound” as the reason if the accountID is greater than hundred.

    return 600;
}

Return a value if the accountID is in between zero and hundred inclusive.

type InquiryFailedErrorData record {|
    string message;
    error cause;
    int accountID;
|};

Error detail type where message and cause are mandatory.

type AccountInquiryFailed error<string, InquiryFailedErrorData>;
function transferToAccount(int fromAccountId, int toAccountId, int amount) returns int|AccountInquiryFailed {
    var balance = getAccountBalance(fromAccountId);
    if (balance is error) {
        AccountInquiryFailed e = error("AccountInquiryFailed", message = balance.reason(), cause = balance, accountID = fromAccountId);
        return e;
    } else {

Create a new error, with the error returned from getAccountBalance() as the cause.

    }

Perform transfer

    return 0;
}
public function main() {
    int|InvalidAccountTypeError result = getTypeId("Joined");
    if (result is int) {
        io:println("Account type ID: ", result);
    } else {
        io:println("Error: ", result.reason(),
                   ", Account type: ", result.detail().accountType);
    }
    var result2 = getAccountBalance(-1);
    if (result2 is int) {
        io:println("Account Balance: ", result2);

If the result is an int, then print the value.

    } else {
        io:println("Error: ", result2.reason(),
                    ", Account ID: ", result2.detail().accountID);
    }

If an error is returned, print the reason and the account ID from the detail record.

    var result3 = transferToAccount(-1, 90, 1000);
    if (result3 is int) {
        io:println("Transfer success: ", result3);
    } else {
        io:println("Error: ", result3.reason(),
                    ", Message: ", result3.detail().message,
                    ", Cause: ", result3.detail().cause);
    }
}

Print the mandatory error detail fields message and cause.

# To run this sample, navigate to the directory that contains the
# `.bal` file, and execute the `ballerina run` command.
$ ballerina run user_defined_error.bal
Error: InvalidAccountType, Account type: Joined
Error: InvalidAccountID, Account ID: -1
Error: AccountInquiryFailed, Message: InvalidAccountID, Cause: error InvalidAccountID accountID=-1