> ## Documentation Index
> Fetch the complete documentation index at: https://developers.stellar.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> Define and generate errors in a smart contract.

The [errors example] demonstrates how to define and generate errors in a contract that invokers of the contract can understand and handle. This example is an extension of the [storing data example].

[![Open in Codespaces](https://github.com/codespaces/badge.svg)][open-in-github-codespaces]

[![Open in Codeanywhere](https://codeanywhere.com/img/open-in-codeanywhere-btn.svg)][open-in-code-anywhere]

[open-in-github-codespaces]: https://github.com/codespaces/new?repo=stellar/soroban-examples&editor=web

[open-in-code-anywhere]: https://app.codeanywhere.com/#https://github.com/stellar/soroban-examples

[errors example]: https://github.com/stellar/soroban-examples/tree/v23.0.0/errors

[storing data example]: https://developers.stellar.org/docs/build/smart-contracts/getting-started/storing-data.md

## Run the Example

First go through the [Setup] process to get your development environment configured, then clone the `v23.0.0` tag of `soroban-examples` repository:

[setup]: https://developers.stellar.org/docs/build/smart-contracts/getting-started/setup.md

```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
git clone -b v23.0.0 https://github.com/stellar/soroban-examples
```

Or, skip the development environment setup and open this example in [GitHub Codespaces][open-in-github-codespaces] or [Code Anywhere][open-in-code-anywhere].

To run the tests for the example, navigate to the `errors` directory, and use `cargo test`.

```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
cd errors
cargo test
```

You should see output that begins like this:

```
running 2 tests
test test::test ... ok
test test::test_panic - should panic ... ok

[Diagnostic Event] contract:CAAA..., topics:[log], data:["count: {}", 0]
[Diagnostic Event] contract:CAAA..., topics:[log], data:["count: {}", 1]
[Diagnostic Event] contract:CAAA..., topics:[log], data:["count: {}", 2]
[Diagnostic Event] contract:CAAA..., topics:[log], data:["count: {}", 3]
[Diagnostic Event] contract:CAAA..., topics:[log], data:["count: {}", 4]
[Diagnostic Event] contract:CAAA..., topics:[log], data:["count: {}", 5]
[Failed Diagnostic Event (not emitted)] contract:CAAA..., topics:[log], data:["count: {}", 5]

thread 'test::test_panic' panicked at .../src/host.rs: HostError: Error(Contract, #1)

Event log (newest first):
   0: [Diagnostic Event] topics:[error, Error(Contract, #1)], data:"escalating error to panic"
   1: [Diagnostic Event] topics:[error, Error(Contract, #1)], data:["contract call failed", increment, []]
   2: [Failed Diagnostic Event (not emitted)] contract:CAAA..., topics:[error, Error(Contract, #1)], data:"escalating Ok(ScErrorType::Contract) frame-exit to Err"
   3: [Failed Diagnostic Event (not emitted)] contract:CAAA..., topics:[fn_return, increment], data:Error(Contract, #1)
   4: [Failed Diagnostic Event (not emitted)] contract:CAAA..., topics:[log], data:["count: {}", 5]
   5: [Diagnostic Event] topics:[fn_call, CAAA..., increment], data:Void
...

successes:
    test::test
    test::test_panic

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.16s
```

## Code

```rust title="errors/src/lib.rs" theme={"theme":{"light":"github-light","dark":"github-dark"}}
#[contracterror]
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[repr(u32)]
pub enum Error {
    LimitReached = 1,
}

const COUNTER: Symbol = symbol_short!("COUNTER");
const MAX: u32 = 5;

#[contract]
pub struct IncrementContract;

#[contractimpl]
impl IncrementContract {
    /// Increment increments an internal counter, and returns the value. Errors
    /// if the value is attempted to be incremented past 5.
    pub fn increment(env: Env) -> Result<u32, Error> {
        // Get the current count.
        let mut count: u32 = env.storage().instance().get(&COUNTER).unwrap_or(0); // If no value set, assume 0.
        log!(&env, "count: {}", count);

        // Increment the count.
        count += 1;

        // Check if the count exceeds the max.
        if count <= MAX {
            // Save the count.
            env.storage().instance().set(&COUNTER, &count);

            // Return the count to the caller.
            Ok(count)
        } else {
            // Return an error if the max is exceeded.
            Err(Error::LimitReached)
        }
    }
}
```

Ref: [https://github.com/stellar/soroban-examples/tree/v23.0.0/errors](https://github.com/stellar/soroban-examples/tree/v23.0.0/errors)

## How it Works

Open the [`errors/src/lib.rs`](https://github.com/stellar/soroban-examples/tree/v23.0.0/errors/src/lib.rs) file to follow along.

### Defining an Error

Contract errors are Rust u32 enums where every variant of the enum is assigned an integer. The `#[contracterror]` attribute is used to set the error up so it can be used in the return value of contract functions.

The enum has some constraints:

* It must have the `#[repr(u32)]` attribute.
* It must have the `#[derive(Copy)]` attribute.
* Every variant must have an explicit integer value assigned.

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
#[contracterror]
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[repr(u32)]
pub enum Error {
    LimitReached = 1,
}
```

Contract errors cannot be stored as contract data, and therefore cannot be used as types on fields of contract types.

> [!TIP]
>
> If an error is returned from a function anything the function has done is rolled back. If ledger entries have been altered, or contract data stored, all those changes are reverted and will not be persisted.

### Returning an Error

Errors can be returned from contract functions by returning `Result<_, E>`.

The increment function returns a `Result<u32, Error>`, which means it returns `Ok(u32)` in the successful case, and `Err(Error)` in the error case.

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
pub fn increment(env: Env) -> Result<u32, Error> {
    // ...
    if count <= MAX {
        // ...
        Ok(count)
    } else {
        // ...
        Err(Error::LimitReached)
    }
}
```

### Panicking with an Error

Errors can also be panicked instead of being returned from the function.

The increment function could also be written as follows with a `u32` return value. The error can be passed to the environment using the `panic_with_error!` macro.

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
pub fn increment(env: Env) -> u32 {
    // ...
    if count <= MAX {
        // ...
        count
    } else {
        // ...
        panic_with_error!(&env, Error::LimitReached)
    }
}
```

> [!WARNING]
>
> Functions that do not return a `Result<_, E>` type do not include in their specification what the possible error values are. This makes it more difficult for other contracts and clients to integrate with the contract. However, this might be ideal if the errors are diagnostic and debugging, and not intended to be handled.

## Tests

Open the [`errors/src/test.rs`](https://github.com/stellar/soroban-examples/tree/v23.0.0/errors/src/test.rs) file to follow along.

```rust title="errors/src/test.rs" theme={"theme":{"light":"github-light","dark":"github-dark"}}
#[test]
fn test() {
    let env = Env::default();
    let contract_id = env.register(IncrementContract, ());
    let client = IncrementContractClient::new(&env, &contract_id);

    assert_eq!(client.try_increment(), Ok(Ok(1)));
    assert_eq!(client.try_increment(), Ok(Ok(2)));
    assert_eq!(client.try_increment(), Ok(Ok(3)));
    assert_eq!(client.try_increment(), Ok(Ok(4)));
    assert_eq!(client.try_increment(), Ok(Ok(5)));
    assert_eq!(client.try_increment(), Err(Ok(Error::LimitReached)));

    std::println!("{}", env.logs().all().join("\n"));
}

#[test]
#[should_panic(expected = "HostError: Error(Contract, #1)")]
fn test_panic() {
    let env = Env::default();
    let contract_id = env.register(IncrementContract, ());
    let client = IncrementContractClient::new(&env, &contract_id);

    assert_eq!(client.increment(), 1);
    assert_eq!(client.increment(), 2);
    assert_eq!(client.increment(), 3);
    assert_eq!(client.increment(), 4);
    assert_eq!(client.increment(), 5);
    client.increment();
}
```

In any test the first thing that is always required is an `Env`, which is the Soroban environment that the contract will run in.

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
let env = Env::default();
```

The contract is registered with the environment using the contract type.

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
let contract_id = env.register(IncrementContract, ());
```

All public functions within an `impl` block that is annotated with the `#[contractimpl]` attribute have a corresponding function generated in a generated client type. The client type will be named the same as the contract type with `Client` appended. For example, in our contract the contract type is `IncrementContract`, and the client is named `IncrementContractClient`.

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
let client = IncrementContractClient::new(&env, &contract_id);
```

Two functions are generated for every contract function, one that returns a `Result<>`, and the other that does not handle errors and panics if an error occurs.

### `try_increment`

In the first test the `try_increment` function is called and returns `Result<Result<u32, _>, Result<Error, InvokeError>>`.

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
assert_eq!(client.try_increment(), Ok(Ok(5)));
assert_eq!(client.try_increment(), Err(Ok(Error::LimitReached)));
```

* If the function call is successful, `Ok(Ok(u32))` is returned.
* If the function call is successful but returns a value that is not a `u32`, `Ok(Err(_))` is returned.
* If the function call is unsuccessful and fails with an error in the `Error` enum, `Err(Ok(Error))` is returned.
* If the function call is unsuccessful but returns an error code not in the `Error` enum, or returns a system error code, `Err(Err(InvokeError))` is returned and the `InvokeError` can be inspected.

### `increment`

In the second test the `increment` function is called and returns `u32`. When the last call is made the function panics.

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
assert_eq!(client.increment(), 5);
client.increment();
```

* If the function call is successful, `u32` is returned.
* If the function call is successful but returns a value that is not a `u32`, a panic occurs.
* If the function call is unsuccessful, a panic occurs.

## Build the Contract

To build the contract, use the `stellar contract build` command.

```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
stellar contract build
```

A `.wasm` file should be outputted in the `target` directory:

```
target/wasm32v1-none/release/soroban_errors_contract.wasm
```

## Run the Contract

Let's deploy the contract to Testnet so we can run it. The value provided as `--source-account` was set up in our Getting Started guide; please change accordingly if you created a different identity.

> [tab] macOS/Linux
>
> ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
> stellar contract deploy \
>   --wasm target/wasm32v1-none/release/soroban_errors_contract.wasm \
>   --source-account alice \
>   --network testnet
> ```

> [tab] Windows (PowerShell)
>
> ```powershell theme={"theme":{"light":"github-light","dark":"github-dark"}}
> stellar contract deploy `
>   --wasm target/wasm32v1-none/release/soroban_errors_contract.wasm `
>   --source-account alice `
>   --network testnet
> ```

The command above will output the contract id, which in our case is `CA4KWO3HL6M5F5MZ5ITVFLCQ6ZM2GDSP2NOTNXOT4GIRECQOVX3I6CXL`.

Now that we've deployed the contract, we can invoke it.

> [tab] macOS/Linux
>
> ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
> stellar contract invoke \
>     --id CA4KWO3HL6M5F5MZ5ITVFLCQ6ZM2GDSP2NOTNXOT4GIRECQOVX3I6CXL \
>     --network testnet \
>     --source-account alice \
>     -- \
>     increment
> ```

> [tab] Windows (PowerShell)
>
> ```powershell theme={"theme":{"light":"github-light","dark":"github-dark"}}
> stellar contract invoke `
>     --id CA4KWO3HL6M5F5MZ5ITVFLCQ6ZM2GDSP2NOTNXOT4GIRECQOVX3I6CXL `
>     --network testnet `
>     --source-account alice `
>     -- `
>     increment
> ```

Run the command a few times and on the 6th invocation you should see an error like this:

```
❌ error: transaction simulation failed: HostError: Error(Contract, #1)

Event log (newest first):
   0: [Diagnostic Event] contract:CA4KWO3HL6M5F5MZ5ITVFLCQ6ZM2GDSP2NOTNXOT4GIRECQOVX3I6CXL, topics:[error, Error(Contract, #1)], data:"escalating Ok(ScErrorType::Contract) frame-exit to Err"
   1: [Diagnostic Event] topics:[fn_call, CA4KWO3HL6M5F5MZ5ITVFLCQ6ZM2GDSP2NOTNXOT4GIRECQOVX3I6CXL, increment], data:Void
```

To retrieve the current counter value, use the command `stellar contract read`.

> [tab] macOS/Linux
>
> ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
> stellar contract read \
>   --id CA4KWO3HL6M5F5MZ5ITVFLCQ6ZM2GDSP2NOTNXOT4GIRECQOVX3I6CXL \
>   --network testnet \
>   --source-account alice \
>   --durability persistent \
>   --output json
> ```

> [tab] Windows (PowerShell)
>
> ```powershell theme={"theme":{"light":"github-light","dark":"github-dark"}}
> stellar contract read `
>   --id CA4KWO3HL6M5F5MZ5ITVFLCQ6ZM2GDSP2NOTNXOT4GIRECQOVX3I6CXL `
>   --network testnet `
>   --source-account alice `
>   --durability persistent `
>   --output json
> ```

[`stellar-cli`]: ../getting-started/setup#install-the-stellar-cli
