Terminating TVM with exit code 33. Debugging this error
Tags: blockchain, debugging, ton, errors
Published: 2023-06-21 14:16:05

![error](https://res.cloudinary.com/djjd0wi5i/image/upload/v1687356720/wonder_ooowgk.jpg)

In this article I will try to debug and understand and error that I've encountered a couple of times, while sending transactions in the [Telegram Open Network](https://ton.org). I'll describe you a little bit my steps for debugging this *weird error*, maybe is useful for someone else who faced the same issue. As always I'll put a way to avoid this error. Let's start.


## TL;DR

The error is due to the [seqno](https://docs.ton.org/develop/dapps/asset-processing/#seqno-based-wallets) been sent when making a transaction does not match the current **seqno** in the wallet smart contract. For more details, checkout the whole article.

## Requirement

Some knowledge of FunC and patience to read assembly of the [TVM](https://docs.ton.org/learn/tvm-instructions/tvm-overview) :).

## Context

I encountered this errors while trying to send several transactions to different [Anonymous Numbers](https://fragment.com/numbers) which I own. The transactions were meant to put all those numbers into auction in [Fragment Marketplace](https://fragment.com/numbers). The error is not tied to the [telemint contract](https://github.com/TelegramMessenger/telemint) itself. Let me describe you the error so you get a better grasp.


## Error

The error logs that you will receive could be similar to this one

```bash

panic: failed to send message: lite server error, code 0: cannot apply external message to current state : External message was not accepted
Cannot run message on account: inbound external message rejected by transaction DCEFC589BFF751F2165B3381BB72552B36186CFDBAF06885BF4CE07BA677ECD0:
exitcode=33, steps=23, gas_used=0
VM Log (truncated):
...te NOW
execute LEQ
execute THROWIF 36
execute PUSH c4
execute CTOS
execute LDU 32
execute LDU 32
execute LDU 256
execute LDDICT
execute ENDS
execute XCPU s4,s3
execute EQUAL
execute THROWIFNOT 33
default exception handler, terminating vm with exit code 33

```

Now let's go part by part trying to understand this error log. This message can be divided in two parts:

**General description**

In this part we can see that the main issue seems to be that the **liteserver** *cannot apply external message to current state*. But what does this really means? I'll try to shed some lights to this, honestly will be all educated guesses because I'm not a core developer of TON blockchain, so no idea how **liteservers** are implemented.

Let's remember what is the flow of a transaction when we make use of a library to send this transaction. Should be something like this:

```bash

[Library for example tonutils-go] ==> [liteserver] ==> [external message to wallet contract] ==> [from wallet contract to destination address]

```

From our error message we can guess that the issue is when the **liteserver** try to send the [external message](https://docs.ton.org/develop/smart-contracts/guidelines/external-messages) to the wallet contract. This can give us the idea that some check is failing in the wallet contract. Looking at the wallet contract code, you can check the following lines:

```C

  throw_if(36, valid_until <= now());
  var ds = get_data().begin_parse();
  var (stored_seqno, stored_subwallet, public_key, plugins) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256), ds~load_dict());
  ds.end_parse();
  throw_unless(33, msg_seqno == stored_seqno);
  
```

So our error is the 33, which seems to be related to the `seqno`. To be sure we are in the right path, you can try to analyze the assembly logs and see if it match with these code. You can skip the next part if you don't want to know all that, can be useful tho, for future errors.


**Truncated assembly execution**

Let's try to figure out how would be these instructions in [FunC](https://docs.ton.org/develop/func/overview). Let's break these instructions

```asm

execute NOW
execute LEQ
execute THROWIF 36

```

First we have a `NOW` instruction which will retrieve the current time, followed by
`LEQ`  which according to the TVM paper is just a **<=** comparison, and at the end a `THROWIF 36`. This can be put in this way

```C

throw_if(36, some_value <= now());

```

The second part of the assembly language is as follows

```asm

execute PUSH c4
execute CTOS
execute LDU 32
execute LDU 32
execute LDU 256
execute LDDICT
execute ENDS

```

We start this part with a `PUSH C4`, here is quite interesting that the `C4` register contains the persistent data of the smart contract. Then with `PUSH C4` we are just **accessing this persistent data** of the smart contract.

Next to that we have a `CTOS` which will convert a cell into a slice followed by `LDU`, `LDDICT` and `ENDS` instructions. Here we can make an educated guess that these part could be something like this.

```C

var ds = get_data().begin_parse()
ds.load_uint(v1, 32)
ds.load_uint(v2, 32)
ds.load_uint(v2, 256) ;; this is an address instead
ds.load_dict()
end_parse()

```

In general would be the parsing of the **c4 register**(contract data). The last part would be as follows


```asm

execute XCPU s4,s3
execute EQUAL
execute THROWIFNOT 33

```

In this part we have the `XCPU` instruction which is equivalent to `XCHG s4` followed by a `PUSH s3`. The key here is to notice that the fourth value of the stack **s4** will be exchange with **s0**(the element in the top now) and later **s3** will be pushed in the top of the stack so will end up with **s3** and **s4** in the top of the stack. Take a look at this sketch, maybe can be helpful in this part

![XCPU stack flow](https://res.cloudinary.com/djjd0wi5i/image/upload/v1687348713/stack_emou92.jpg)

Now having this, the following instruction would be `EQUAL` which will basically compare if the olds **s3** and **s4** are equal. In case they are not we will throw an error 33 with `THROWIFNOT 33`. In FunC this could be written as follows

```C

throw_unless(33, s3 == s4);

```

Here is the key of our problem, we cannot infer anything else from this code so we have

```C

;; check time
throw_if(36, some_value <= now());

;; parse contract data
var ds = get_data().begin_parse()
ds.load_uint(v1, 32)
ds.load_uint(v2, 32)
ds.load_uint(v2, 256) ;; this is an address
ds.load_dict()
end_parse()

throw_unless(33, s3 == s4);

```


## Partial conclusion

From all this analysis we can assume the issue is that the `seqno` has been sent in the message differs with the one stored in the wallet contract. This could happens in the following scenario, which was my case. 

We have two clients sending a message to the wallet contract at the same time, both of them ask for a `seqno` to the [seqno](https://github.com/ton-blockchain/wallet-contract/blob/4111fd9e3313ec17d99ca9b5b1656445b5b49d8f/func/wallet-v4-code.fc#L166) method. The contract returns the stored `seqno` at that moment.

![seqno part 1](https://res.cloudinary.com/djjd0wi5i/image/upload/v1687354144/seqno_1_eimdy8.jpg)

Now at this moment both clients have a `seqno = 1`, they both try to make a request with a `seqno = 1`. The faster of both will make the contract update the seqno to `seqno = 2`, so when the slower one will try to send the message the wallet contract will throw the following exception.

```c

throw_unless(33, msg_seqno == stored_seqno);

```

![seqno part 2](https://res.cloudinary.com/djjd0wi5i/image/upload/v1687354144/seqno_2_vatzmz.jpg)


## Conclusion

Just try to not send two messages with the same seqno 😂. The life of a programmer😂. I spent a hell debugging this error, to realize that I was making a basic error.

## Wrong paths

While I was debugging this error I took a wrong path at first instance. I will describe it here, so you might face this issue and is not related to the seqno 😂. The other possibility that I analyzed was the possibility that I was sending a transaction that triggered this error.

[action list too long](https://github.com/ton-blockchain/ton/blob/cc0eb453cb3bf69f92693160103d33112856c056/crypto/block/transaction.cpp#L1209)

This was quite complicated because is the code of the ton blockchain, so it gave me a lot of headaches. This error is triggered when amount of `output actions` pass the limit, which is 255. In case this is your error, I suggest you some links that might be useful, but honestly try to analyze first if it is a seqno issue, because is way more simple.

**Links:**

1. [Result of tvm execution](https://docs.ton.org/learn/tvm-instructions/tvm-overview#result-of-tvm-execution)
2. [Outbound message and output action primitives](https://docs.ton.org/learn/tvm-instructions/instructions#119-outbound-message-and-output-action-primitives)

## Rule of thumb

Go for the simple hypothesis, and apply the Occam's razor. The problem with this principle is that I always remember it when I tried the hardest path. That's all folks!