Custom errors#

This guide shows how to return custom errors from your Rust smart contract.

Defining and deriving#

Custom error codes help communicate why a contract rejects and can be returned both during initialization and during updates.

On-chain, smart contracts return a numeric error code and an optional serialized return value when rejecting. This is also the case when using a custom error type. Therefore, a mapping from the custom error type to Reject, in the form of an implementation of From<MyError> for Reject, is needed. You can derive the implementation automatically with #[derive(Reject)] if the type also implements Serial (also derivable). The Serial instance is needed because the whole data type is serialized and included as the optional return value. Here is a typical example:

#[derive(Serial, Reject)]
enum MyError {
    ErrOne,
    ErrTwo,
}

Note

The valid range of error codes is i32::MIN..-1. When deriving Reject, each variant is assigned an error code as determined by the ordering. First variant (ErrOne in example) gets -1, second variant (ErrTwo in example) gets -2, and so on.

Warning

Deriving Reject for enums with custom discriminant values is not supported.

Using custom errors#

Return custom errors, as you would with any other error type:

#[init(contract = "my_contract")]
fn contract_init_my(
    _ctx: &InitContext,
    _state_builder: &mut StateBuilder,
) -> Result<State, MyError> { Err(MyError::ErrOne) }

#[receive(contract = "my_contract", name = "my_receive")]
fn contract_receive_my(
    _ctx: &ReceiveContext,
    _host: &Host<State>
) -> Result<MyReturnValue, MyError> { Err(MyError::ErrTwo) }

Note

Adding schemas to your errors makes them more useful in concordium-client and cargo-concordium.

Standard error codes#

The following standard error codes exist:

Variant

Error code

()

i32::MIN + 1 (-2147483647)

ParseError

i32::MIN + 2 (-2147483646)

LogError::Full

i32::MIN + 3 (-2147483645)

LogError::Malformed

i32::MIN + 4 (-2147483644)

NewContractNameError::MissingInitPrefix

i32::MIN + 5 (-2147483643)

NewContractNameError::TooLong

i32::MIN + 6 (-2147483642)

NewContractNameError::ContainsDot

i32::MIN + 9 (-2147483639)

NewContractNameError::InvalidCharacters

i32::MIN + 10 (-2147483638)

NewReceiveNameError::MissingDotSeparator

i32::MIN + 7 (-2147483641)

NewReceiveNameError::TooLong

i32::MIN + 8 (-2147483640)

NewReceiveNameError::InvalidCharacters

i32::MIN + 11 (-2147483637)

NotPayableError

i32::MIN + 12 (-2147483636)

TransferError::AmountTooLarge

i32::MIN + 13 (-2147483635)

TransferError::MissingAccount

i32::MIN + 14 (-2147483634)

CallContractError::AmountTooLarge

i32::MIN + 15 (-2147483633)

CallContractError::MissingAccount

i32::MIN + 16 (-2147483632)

CallContractError::MissingContract

i32::MIN + 17 (-2147483631)

CallContractError::MissingEntrypoint

i32::MIN + 18 (-2147483630)

CallContractError::MessageFailed

i32::MIN + 19 (-2147483629)

CallContractError::LogicReject

i32::MIN + 20 (-2147483628)

CallContractError::Trap

i32::MIN + 21 (-2147483627)

UpgradeError::MissingModule

i32::MIN + 22 (-2147483626)

UpgradeError::MissingContract

i32::MIN + 23 (-2147483625)

UpgradeError::UnsupportedModuleVersion

i32::MIN + 24 (-2147483624)

QueryAccountBalanceError

i32::MIN + 25 (-2147483623)

QueryContractBalanceError

i32::MIN + 26 (-2147483622)

The error codes are also listed in the library documentation on docs.rs.

Was this article helpful?