Skip to content

Exit codes

Each transaction on TON Blockchain consists of multiple phases. An exit code is a 3232-bit signed integer that indicates whether the compute or action phase of the transaction was successful, and if not — holds the code of the exception that occurred. Each exit code represents its own exception or resulting state of the transaction.

Exit codes 00 and 11 indicate normal (successful) execution of the compute phase. Exit (or result) code 00 indicates normal (successful) execution of the action phase. Any other exit code indicates that a certain exception has occurred and that the transaction was not successful in one way or another, i.e. the transaction was reverted or the inbound message has bounced back.

TON Blockchain reserves exit code values from 00 to 127127, while Tact utilizes exit codes from 128128 to 255255. Note that exit codes used by Tact indicate contract errors, which can occur when using Tact-generated FunC code, and are therefore thrown in the transaction’s compute phase and not during compilation.

The range from 256256 to 6553565535 is free for developer-defined exit codes.

Table of exit codes

The following table lists exit codes with their origin (where they can occur) and a short description for each. The table does not list the exit code of the require(), as it generates it depending on the concrete error message String. To see such exit codes, refer to the Exit codes section of the compilation report.

Exit codeOriginBrief description
00Compute and action phasesStandard successful execution exit code.
11Compute phaseAlternative successful execution exit code. Reserved, but does not occur.
22Compute phaseStack underflow.
33Compute phaseStack overflow.
44Compute phaseInteger overflow.
55Compute phaseRange check error — an integer is out of its expected range.
66Compute phaseInvalid TVM opcode.
77Compute phaseType check error.
88Compute phaseCell overflow.
99Compute phaseCell underflow.
1010Compute phaseDictionary error.
1111Compute phaseDescribed in TVM docs as “Unknown error, may be thrown by user programs.”
1212Compute phaseFatal error. Thrown by TVM in situations deemed impossible.
1313Compute phaseOut of gas error.
14-14Compute phaseSame as 1313. Negative, so that it cannot be faked.
1414Compute phaseVM virtualization error. Reserved, but never thrown.
3232Action phaseAction list is invalid.
3333Action phaseAction list is too long.
3434Action phaseAction is invalid or not supported.
3535Action phaseInvalid source address in outbound message.
3636Action phaseInvalid destination address in outbound message.
3737Action phaseNot enough Toncoin.
3838Action phaseNot enough extra currencies.
3939Action phaseOutbound message does not fit into a cell after rewriting.
4040Action phaseCannot process a message — not enough funds, the message is too large, or its Merkle depth is too big.
4141Action phaseLibrary reference is null during library change action.
4242Action phaseLibrary change action error.
4343Action phaseExceeded the maximum number of cells in the library or the maximum depth of the Merkle tree.
5050Action phaseAccount state size exceeded limits.
128128Tact compiler (Compute phase)Null reference exception. Configurable since Tact 1.6.
129129Tact compiler (Compute phase)Invalid serialization prefix.
130130Tact compiler (Compute phase)Invalid incoming message — there is no receiver for the opcode of the received message.
131131Tact compiler (Compute phase)Constraints error. Reserved, but never thrown.
132132Tact compiler (Compute phase)Access denied — someone other than the owner sent a message to the contract.
133133Tact compiler (Compute phase)Contract stopped. Reserved, but never thrown.
134134Tact compiler (Compute phase)Invalid argument.
135135Tact compiler (Compute phase)Code of a contract was not found.
136136Tact compiler (Compute phase)Invalid standard address.
137137Tact compiler (Compute phase)Masterchain support is not enabled for this contract. Removed since Tact 1.6.
138138Tact compiler (Compute phase)Not a basechain address.

Exit codes in Blueprint projects

In Blueprint tests, exit codes from the compute phase are specified in the exitCode field of the object argument for the toHaveTransaction() method of the expect() matcher. The field for the result codes (exit codes from the action phase) in the same toHaveTransaction() method is called actionResultCode.

Additionally, one can examine the result of sending a message to a contract and discover the phases of each transaction and their values, including exit (or result) codes for the compute phase (or action phase).

Note that to do so, you’ll have to perform a couple of type checks first:

it('tests something, you name it', async () => {
// Send a specific message to our contract and store the results
const res = await your_contract_name.send(…);
// Now, we have access to an array of executed transactions,
// with the second one (index 1) being the one we look for
const tx = res.transactions[1]!;
// To do something useful with it, let's ensure that its type is 'generic'
// and that the compute phase in it wasn't skipped
if (tx.description.type === "generic"
&& tx.description.computePhase.type === "vm") {
// Finally, we're able to freely peek into the transaction for general details,
// such as printing out the exit code of the compute phase if we so desire
console.log(tx.description.computePhase.exitCode);
}
// ...
});

Compute and action phases

0: Normal termination

This exit (or result) code indicates the successful completion of the compute phase (or action phase) of the transaction.

Compute phase

TVM initialization and all computations occur in the compute phase.

If the compute phase fails (the resulting exit code is neither 00 nor 11), the transaction skips the action phase and proceeds to the bounce phase. In this phase, a bounce message is formed for transactions initiated by the inbound message.

1: Alternative termination

This is an alternative exit code for the successful execution of the compute phase. It is reserved but never occurs.

2: Stack underflow

If an operation consumes more elements than exist on the stack, an error with exit code 22 is thrown: Stack underflow.

asm fun drop() { DROP }
contract Loot {
receive("I solemnly swear that I'm up to no good") {
try {
// Removes 100 elements from the stack, causing an underflow
repeat (100) { drop() }
} catch (exitCode) {
// exitCode is 2
}
}
}

3: Stack overflow

If there are too many elements copied into a closure continuation, an error with exit code 33 is thrown: Stack overflow. This occurs rarely unless you’re deep in the Fift and TVM assembly trenches:

// Remember kids, don't try to overflow the stack at home!
asm fun stackOverflow() {
x{} SLICE // s
BLESS // c
0 SETNUMARGS // c'
2 PUSHINT // c' 2
SWAP // 2 c'
1 -1 SETCONTARGS // ← this blows up
}
contract ItsSoOver {
receive("I solemnly swear that I'm up to no good") {
try {
stackOverflow();
} catch (exitCode) {
// exitCode is 3
}
}
}

4: Integer overflow

If the value in a calculation goes beyond the range from 2256-2^{256} to 225612^{256} - 1 inclusive, or there’s an attempt to divide or perform modulo by zero, an error with exit code 44 is thrown: Integer overflow.

let x = -pow(2, 255) - pow(2, 255); // -2^{256}
try {
-x; // integer overflow by negation
// since the max positive value is 2^{256} - 1
} catch (exitCode) {
// exitCode is 4
}
try {
x / 0; // division by zero!
} catch (exitCode) {
// exitCode is 4
}
try {
x * x * x; // integer overflow!
} catch (exitCode) {
// exitCode is 4
}
// There can also be an integer overflow when performing:
// addition (+),
// subtraction (-),
// division (/) by a negative number or modulo (%) by zero

5: Integer out of expected range

A range check error occurs when some integer is out of its expected range. Any attempt to store an unexpected amount of data or specify an out-of-bounds value throws an error with exit code 55: Integer out of expected range.

Examples of specifying an out-of-bounds value:

try {
// Repeat only operates on an inclusive range from 1 to 2^{31} - 1
// Any valid integer value greater than that causes an error with exit code 5
repeat (pow(2, 55)) {
dump("smash. logs. I. must.");
}
} catch (exitCode) {
// exitCode is 5
}
try {
// Builder.storeUint() function can only use up to 256 bits, thus 512 is too much:
let s: Slice = beginCell().storeUint(-1, 512).asSlice();
} catch (exitCode) {
// exitCode is 5
}

6: Invalid opcode

If you specify an instruction that is not defined in the current TVM version or attempt to set an unsupported code page, an error with exit code 66 is thrown: Invalid opcode.

// There's no such code page, and an attempt to set it fails
asm fun invalidOpcode() { 42 SETCP }
contract OpOp {
receive("I solemnly swear that I'm up to no good") {
try {
invalidOpcode();
} catch (exitCode) {
// exitCode is 6
}
}
}

7: Type check error

If an argument to a primitive is of an incorrect value type or there is any other mismatch in types during the compute phase, an error with exit code 77 is thrown: Type check error.

// The actual returned value type doesn't match the declared one
asm fun typeCheckError(): map<Int, Int> { 42 PUSHINT }
contract VibeCheck {
receive("I solemnly swear that I'm up to no good") {
try {
// The 0th index doesn't exist
typeCheckError().get(0)!!;
} catch (exitCode) {
// exitCode is 7
}
}
}

8: Cell overflow

From the Cells, Builders and Slices page of the Book:

Cell is a primitive and a data structure, which ordinarily consists of up to 1023 continuously laid out bits and up to 4 references (refs) to other cells.

To construct a Cell, a Builder is used. If you try to store more than 1023 bits of data or more than 4 references to other cells, an error with exit code 88 is thrown: Cell overflow.

This error can be triggered by manual construction of the cells via relevant .loadSomething() methods or when using Structs and Messages and their convenience methods.

// Too many bits
try {
let data = beginCell()
.storeInt(0, 250)
.storeInt(0, 250)
.storeInt(0, 250)
.storeInt(0, 250)
.storeInt(0, 24) // 1024 bits!
.endCell();
} catch (exitCode) {
// exitCode is 8
}
// Too many refs
try {
let data = beginCell()
.storeRef(emptyCell())
.storeRef(emptyCell())
.storeRef(emptyCell())
.storeRef(emptyCell())
.storeRef(emptyCell()) // 5 refs!
.endCell();
} catch (exitCode) {
// exitCode is 8
}

9: Cell underflow

From the Cells, Builders and Slices page of the Book:

Cell is a primitive and a data structure, which ordinarily consists of up to 1023 continuously laid-out bits and up to 4 references (refs) to other cells.

To parse a Cell, a Slice is used. If you try to load more data or references than a Slice contains, an error with exit code 9 is thrown: Cell underflow.

The most common cause of this error is a mismatch between the expected and actual memory layouts of the cells, so it’s recommended to use Structs and Messages for parsing the cells instead of manual parsing via relevant .loadSomething() methods.

// Too few bits
try {
emptySlice().loadInt(1); // 0 bits!
} catch (exitCode) {
// exitCode is 9
}
// Too few refs
try {
emptySlice().loadRef(); // 0 refs!
} catch (exitCode) {
// exitCode is 9
}

10: Dictionary error

In Tact, the map<K, V> type is an abstraction over the “hash” map dictionaries of FunC and the underlying HashmapE type of TL-B and TVM.

If there is incorrect manipulation of dictionaries, such as improper assumptions about their memory layout, an error with exit code 1010 is thrown: Dictionary error. Note that Tact prevents you from getting this error unless you perform Fift and TVM assembly work yourself:

/// Pre-computed Int to Int dictionary with two entries — 0: 0 and 1: 1
const cellWithDictIntInt: Cell = cell("te6cckEBBAEAUAABAcABAgPQCAIDAEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAQQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMLMbT1U=");
/// Tries to preload a dictionary from a Slice as a map<Int, Cell>
asm fun toMapIntCell(x: Slice): map<Int, Cell> { PLDDICT }
contract DictPic {
receive("I solemnly swear that I'm up to no good") {
try {
// The Int to Int dictionary is being misinterpreted as a map<Int, Cell>
let m: map<Int, Cell> = toMapIntCell(cellWithDictIntInt.beginParse());
// And the error happens only when we touch it
m.get(0)!!;
} catch (exitCode) {
// exitCode is 10
}
}
}

11: “Unknown” error

Described in the TVM docs as “Unknown error, may be thrown by user programs,” although most commonly used for problems with queuing a message send or problems with get-methods.

try {
// Unlike nativeSendMessage which uses SENDRAWMSG, this one uses SENDMSG,
// and therefore fails in Compute phase when the message is ill-formed
nativeSendMessageReturnForwardFee(emptyCell(), 0);
} catch (exitCode) {
// exitCode is 11
}

12: Fatal error

Fatal error. Thrown by TVM in situations deemed impossible.

13: Out of gas error

If there isn’t enough gas to complete computations in the compute phase, an error with exit code 1313 is thrown: Out of gas error.

However, this code isn’t immediately shown as is — instead, the bitwise NOT operation is applied, changing the value from 1313 to 14-14. Only then is the code displayed.

This is done to prevent the resulting code (14-14) from being produced artificially in user contracts, as all functions that can throw an exit code can only specify integers in the range from 00 to 6553565535 inclusive.

try {
repeat (pow(2, 31) - 1) {}
} catch (exitCode) {
// exitCode is -14
}

-14: Out of gas error

See exit code 13.

14: Virtualization error

Virtualization error related to pruned branch cells. Reserved but never thrown.

Action phase

The action phase is processed after the successful execution of the compute phase. It attempts to perform the actions stored in the action list by TVM during the compute phase.

Some actions may fail during processing, in which case those actions may be skipped or the whole transaction may revert depending on the mode of actions. The code indicating the resulting state of the action phase is called a result code. Since it is also a 32-bit signed integer that essentially serves the same purpose as the exit code of the compute phase, it is common to call the result code an exit code as well.

32: Action list is invalid

If the list of actions contains exotic cells, an action entry cell does not have references, or some action entry cell cannot be parsed, an error with exit code 32 is thrown: Action list is invalid.

33: Action list is too long

If there are more than 255 actions queued for execution, the action phase will throw an error with an exit code 33: Action list is too long.

// For example, let's attempt to queue reservation of a specific amount of nanoToncoins
// This won't fail in the compute phase, but will result in exit code 33 in the action phase
repeat (256) {
nativeReserve(ton("0.001"), ReserveAtMost);
}

34: Invalid or unsupported action

There are only four supported actions at the moment: changing the contract code, sending a message, reserving a specific amount of nanoToncoins, and changing the library cell. If there is any issue with the specified action (invalid message, unsupported action, etc.), an error with exit code 3434 is thrown: Invalid or unsupported action.

// For example, let's try to send an ill-formed message:
nativeSendMessage(emptyCell(), 0); // won't fail in the compute phase,
// but will result in exit code 34 in the Action phase

35: Invalid source address in outbound message

If the source address in the outbound message is not equal to addr_none or to the address of the contract that initiated this message, an error with exit code 3535 is thrown: Invalid source address in outbound message.

36: Invalid destination address in outbound message

If the destination address in the outbound message is invalid, e.g., it does not conform to the relevant TL-B schemas, contains an unknown workchain ID, or has an invalid length for the given workchain, an error with exit code 3636 is thrown: Invalid destination address in outbound message.

37: Not enough Toncoin

If all funds of the inbound message with base mode 64 set have already been consumed and there are not enough funds to pay for the failed action, or the TL-B layout of the provided value (CurrencyCollection) is invalid, or there are not enough funds to pay forward fees or not enough funds after deducting fees, an error with exit code 3737 is thrown: Not enough Toncoin.

38: Not enough extra currencies

Besides the native currency, Toncoin, TON Blockchain supports up to 2322^{32} extra currencies. They differ from creating new Jettons because extra currencies are natively supported — one can potentially just specify an extra HashmapE of extra currency amounts in addition to the Toncoin amount in the internal message to another contract. Unlike Jettons, extra currencies can only be stored and transferred and do not have any other functionality.

At the moment, there are no extra currencies on TON Blockchain, but the exit code 3838 for cases when there is not enough extra currency to send the specified amount is already reserved: Not enough extra currencies.

39: Outbound message doesn’t fit into a cell

When processing the message, TON Blockchain tries to pack it according to the relevant TL-B schemas, and if it cannot, an error with exit code 3939 is thrown: Outbound message doesn't fit into a cell.

40: Cannot process a message

If there are not enough funds to process all the cells in a message, the message is too large, or its Merkle depth is too big, an error with exit code 4040 is thrown: Cannot process a message.

41: Library reference is null

If a library reference is required during a library change action but is null, an error with exit code 4141 is thrown: Library reference is null.

42: Library change action error

If there’s an error during an attempt at a library change action, an error with exit code 4242 is thrown: Library change action error.

43: Library limits exceeded

If the maximum number of cells in the library is exceeded or the maximum depth of the Merkle tree is exceeded, an error with exit code 4343 is thrown: Library limits exceeded.

50: Account state size exceeded limits

If the account state (contract storage, essentially) exceeds any of the limits specified in config param 43 of TON Blockchain by the end of the action phase, an error with exit code 5050 is thrown: Account state size exceeded limits.

If the configuration is absent, the default values are:

  • max_msg_bits is equal to 2212^{21} — maximum message size in bits.
  • max_msg_cells is equal to 2132^{13} — maximum number of cells a message can occupy.
  • max_library_cells is equal to 10001000 — maximum number of cells that can be used as library reference cells.
  • max_vm_data_depth is equal to 292^{9} — maximum cells depth in messages and account state.
  • ext_msg_limits.max_size is equal to 6553565535 — maximum external message size in bits.
  • ext_msg_limits.max_depth is equal to 292^{9} — maximum external message depth.
  • max_acc_state_cells is equal to 2162^{16} — maximum number of cells that an account state can occupy.
  • max_acc_state_bits is equal to 216×10232^{16} \times 1023 — maximum account state size in bits.
  • max_acc_public_libraries is equal to 282^{8} — maximum number of library reference cells that an account state can use on the masterchain.
  • defer_out_queue_size_limit is equal to 282^{8} — maximum number of outbound messages to be queued (regarding validators and collators).

Tact compiler

Tact utilizes exit codes from 128128 to 255255. Note that exit codes used by Tact indicate contract errors, which can occur when using Tact-generated FunC code and are therefore thrown in the transaction’s compute phase, not during compilation.

128: Null reference exception

If there is a non-null assertion, such as the !! operator, and the checked value is null, an error with exit code 128128 is thrown: Null reference exception.

This behavior can be modified by setting the nullChecks option to false, which will disable runtime null checks and would allow to save some gas. However, it is recommended to use it only for well-tested contracts, as it can lead to less recognizable type errors reported by TVM.

let gotcha: String? = null;
try {
// Asserting that the value isn't null, which isn't the case!
dump(gotcha!!);
} catch (exitCode) {
// exitCode is 128
}

129: Invalid serialization prefix

Reserved, but due to a number of prior checks, it cannot be thrown unless one hijacks the contract code before deployment and changes the opcodes of the Messages expected to be received in the contract.

130: Invalid incoming message

If the received internal or external message is not handled by the contract, an error with exit code 130130 is thrown: Invalid incoming message. It usually happens when the contract does not have a receiver for the particular message and its opcode prefix (32-bit integer header).

Consider the following contract:

import "@stdlib/deploy";
contract Dummy with Deployable {}

If you try to send any message except for Deploy, provided by @stdlib/deploy, the contract will not have a receiver for it and thus will throw an error with exit code 130130.

131: Constraints error

Constraints error. Reserved, but never thrown.

132: Access denied

If you use the Ownable trait from the @stdlib/ownable library, the helper function requireOwner() provided by it will throw an error with exit code 132132 if the sender of the inbound message does not match the specified owner: Access denied.

import "@stdlib/ownable";
contract Hal9k with Ownable {
owner: Address;
init(owner: Address) {
self.owner = owner; // set the owner address upon deployment
}
receive("I'm sorry Dave, I'm afraid I can't do that.") {
// Checks that the message sender's address equals the owner address,
// and if not — throws an error with exit code 132.
self.requireOwner();
// ... you do you ...
}
}

133: Contract stopped

A message has been sent to a stopped contract. Reserved, but never thrown.

134: Invalid argument

If there is an invalid or unexpected argument value, an error with exit code 134134 is thrown: Invalid argument.

Here are some of the functions in Tact which can throw an error with this exit code:

  1. Int.toFloatString(digits): if digits is not in the interval 0<digits<780 < digits < 78.

  2. String.fromBase64() and Slice.fromBase64(): if the given String or Slice contains non-Base64 characters.

try {
// 0 is the code of NUL in ASCII, and it is not valid Base64
let code: Slice = beginCell().storeUint(0, 8).asSlice().fromBase64();
} catch (exitCode) {
// exitCode is 134
}

135: Code of a contract was not found

If the code of the contract does not match the one saved in TypeScript wrappers, an error with exit code 135135 will be thrown: Code of a contract was not found.

136: Invalid standard address

A value of type Address is considered a valid internal standard address if:

Such checks are currently only performed in the Slice.asAddress() function. If any of the above is false, the function throws an error with exit code 136136: Invalid standard address.

let basechainID = 0;
let addrSlice = beginCell()
.storeUint(0b10_1, 3) // unsupported prefix!
.storeInt(basechainID, 8) // chain ID 0
.storeUint(0, 42) // and a wrong size of the account ID!
.asSlice();
try {
// The tag prefix and account ID are both incorrect
dump(addrSlice.asAddress(basechainID));
} catch (exitCode) {
// exitCode is 136
}

137: Masterchain support is not enabled for this contract

Removed since Tact 1.6

Prior to removal, any attempts to point to the masterchain (ID 1-1) or otherwise interact with it without enabling masterchain support threw an exception with exit code 137137: Masterchain support is not enabled for this contract.

let masterchainID = -1;
try {
// Zero address in masterchain without the config option set
dump(newAddress(masterchainID, 0));
} catch (exitCode) {
// exitCode is 137
}

138: Not a basechain address

Available since Tact 1.6.3

The value of type Address belongs to a basechain, when its chain ID is equal to 0.

Such check is currently only performed in the forceBasechain() function. If the address does not belong to a basechain, i.e., its chain ID isn’t 0, the function throws an error with exit code 138: Not a basechain address.

let someBasechainAddress: Address =
newAddress(0, 0x83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8);
let someMasterchainAddress: Address =
newAddress(-1, 0x83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8);
// Does not throw because the chain ID is 0
forceBasechain(someBasechainAddress);
try {
// Throws because the chain ID is -1 (masterchain)
forceBasechain(someMasterchainAddress);
} catch (exitCode) {
// exitCode is 138
}