Error Handling
Recoverable Errors
Recoverable errors represent expected faults. Similar to Rust, Sway expresses recoverable errors by using the std::result::Result
enum, letting you propagate or transform the error without immediately reverting the transaction.
To learn more about expressing and handling recoverable errors, see the chapter on Result<T, E>
enum.
Irrecoverable Errors
Irrecoverable errors indicate bugs or violated invariants. They trigger a VM-wide revert that atomically rolls back every state change in the transaction, and it cannot be caught or handled in Sway code, signaling that the program cannot sensibly continue.
panic
Expression
The recommended way of expressing an irrecoverable errors is to use the panic
expression:
if some_error_occurred {
panic "Some error has occurred.";
}
At runtime, the panic
expression aborts and reverts the execution of the entire program. At compile time, for each panic
encountered in code, Sway compiler will generate a unique revert code and create an entry in the ABI JSON errorCodes
section. The generated errorCodes
entry will contain the information about source location at which the panic
occurs, as well as the error message.
This mechanism allows for getting a rich troubleshooting information, without an additional on-chain cost. The generated bytecode will contain only the revert instruction, and the remaining information, the error message and the error location, are stored off-chain, in the ABI JSON file.
For example, let's assume that the above code is situated in the module some_module
, contained within the version v1.2.3
of the package some_package
.
At runtime, the panic
will result in a compiler generated revert code, e.g., 18446744069414584323. At compile time, an entry similar to this will be added to the ABI JSON errorCodes
section:
"errorCodes": {
"18446744069414584323": {
"pos": {
"pkg": "some_package@1.2.3",
"file": "some_module.sw",
"line": 13,
"column": 9
},
"logId": null,
"msg": "Some error has occurred."
},
}
Rust and TypeScript SDK, as well as forc test
, recognize revert codes generated from panic
expressions. E.g., if a Sway unit test fails because of a revert caused by the above panic
line, the forc test
will display the following:
test some_test, "path/to/failing/test.sw":42
revert code: ffffffff00000003
├─ panic message: Some error has occurred.
└─ panicked in: some_package@1.2.3, src/some_module.sw:13:9
Error Types
Passing textual error messages directly as a panic
argument is the most convenient way to provide a helpful error message. It is sufficient for many use-cases. However, often we want:
- to provide an additional runtime information about the error.
- group a certain family of errors together.
For these use-cases, you can use error types. Error types are enums annotated with the #[error_type]
attribute, whose all variants are attributed with the #[error(m = "<error message>")]
attributes. Each variant represent a particular error, and the enum itself the family of errors. The convention is to postfix the names of error type enums with Error
.
For example, let's assume we are checking if a provided Identity
has certain access rights to our contract. The error type enum representing access rights violations could look like:
#[error_type]
pub enum AccessRightError {
#[error(m = "The provided identity is not an administrator.")]
NotAnAdmin: Identity,
#[error(m = "The provided identity is not an owner.")]
NotAnOwner: Identity,
#[error(m = "The provided identity does not have write access.")]
NoWriteAccess: Identity,
}
where each Identity
represents the actual, provided identity.
In code, we can now check for access rights and panic if they are violated:
fn do_something_that_requires_admin_access(admin: Identity) {
if !is_admin(admin) {
panic AccessRightError::NotAnAdmin(admin);
}
// ...
}
Assuming we have a failing test for the above function, the test output will show the error message, but also the provided Identity
. E.g.:
test some_test_for_admin_access, "path/to/failing/test.sw":42
revert code: ffffffff00000007
├─ panic message: The provided identity is not an administrator.
├─ panic value: NotAnAdmin(Address(Address()))
└─ panicked in: some_other_package@0.1.0, src/admin_module.sw:11:9