# Exceptions in Tolk (https://docs-orhepa2tm-ton-core-docs.vercel.app/llms/tolk/syntax/exceptions/content.md)



Tolk supports exceptions and handling them with `try catch`. If an exception is not caught, the program terminates with `errCode`.

In [TVM](/llms/tvm/overview/content.md), exceptions are numbers. There is no `Error` class or exception hierarchy, only [exit codes](/llms/tvm/exit-codes/content.md).

## Error codes [#error-codes]

A simple pattern is to define a constant for each exception that a contract may produce:

```tolk
const ERR_LOW_BALANCE = 200
const ERR_SIGNATURE_MISMATCH = 201
```

Yet, this pattern should not be used in production environments, as it does not scale. Instead, use an `enum` — they are easier to maintain and reference, as enumerations come with exhaustive checks from the compiler:

```tolk
enum ErrCode {
    LowBalance = 200,
    SignatureMismatch,    // implicitly 201
}
```

Use these constants in `throw` and related statements described on this page.

<Callout type="tip" title="&#x22;Use error codes between 64 and 2048&#x22;">
  Lower values are [reserved](/llms/tvm/exit-codes/content.md) by TVM. Larger ones are more gas-expensive.
</Callout>

## `throw` statement [#throw-statement]

To throw an exception unconditionally:

```tolk
throw ERR_CODE;
```

Non-constant expressions such as `throw someVariable` are supported but not recommended. It works, but the compiler cannot determine possible error codes and therefore cannot provide a correct ABI for external callers.

An exception can carry an argument of type [`unknown`](/llms/languages/tolk/types/unknown/content.md):

```tolk
throw (ERR_CODE, errArg);
```

The argument is available in `catch`. Any type can be passed; multi-slot types are packed into a single TVM slot automatically:

## `assert` statement [#assert-statement]

An `assert` is a shorthand for "throw if a condition is not satisfied". It is commonly used when parsing user input:

```tolk
assert (msg.seqno == storage.seqno) throw E_INVALID_SEQNO;
assert (msg.validUntil > blockchain.now()) throw E_EXPIRED;
```

* The long form `assert (condition) throw ERR_CODE` is preferred.
* The short form `assert (condition, ERR_CODE)` exists but is not recommended.

The `condition` must be a [`bool`](/llms/languages/tolk/types/booleans/content.md). Integers are not implicitly converted: use `assert (someInt != 0)` instead of `assert (someInt)`.

An `assert` statement is equivalent to:

```tolk
if (!condition) {
    throw ERR_CODE;
}
```

## TVM implicit throws [#tvm-implicit-throws]

During contract execution, TVM may throw runtime exceptions. For example:

* `slice.loadInt(8)` will fail when the slice is empty.
* `builder.storeRef(cell)` will fail when the builder has 4 references already.
* `tuple.push(value)` will fail when the tuple has 255 elements already.
* etc.

Additionally, an [out-of-gas exception](/llms/tvm/exit-codes/content.md) may occur at **any** point, and it cannot be caught using a [`catch`](#try-catch-statement) block. It can only be prevented by writing [exhaustive testing suites](/llms/contract-dev/testing/overview/content.md).

## `try catch` statement [#try-catch-statement]

The `catch` block is used to handle most runtime errors within the `try` block, except for out-of-gas exceptions:

```tolk
try {
    // ...
} catch (errCode) {
    // errCode is `int`
}
```

* Use the short form `catch { ... }` when `errCode` is not needed.
* Use the long form `catch (errCode, arg)` when the exception may carry an argument produced by `throw (errCode, arg)`. The `arg` is of type `unknown`; use `as` to cast it. If the exception was thrown as `throw errCode`, the argument is `0`.

```tolk
try {
    throw (ERR_LOW_BALANCE, 555);
} catch (errCode, arg) {
    val data = arg as int;    // 555
}

// even complex types can be thrown
try {
    throw (ERR_NOT_FOUND, Point { x: 10, y: 20 });
} catch (errCode, arg) {
    val p = arg as Point;     // { x: 10, y: 20 }
}
```

<Callout type="caution">
  Any error inside a `try` block reverts all changes made within. TVM restores local variables and control registers to the state they had before entering `try`.

  However, gas counters and TVM codepage settings are **not** rolled back. Gas is always spent.
</Callout>
