Error Handling and Return Codes
Use this page when you need to decide how a Rust program reports success or failure, and how to read the codes it gets back.
Success and revert
Section titled “Success and revert”An entry function returning Result<u64, u64> ends the program through the exit syscall:
| Return | Effect |
|---|---|
Ok(code) | Exit with code, no revert. State changes are kept. |
Err(code) | Exit with code and the revert flag set. State changes are discarded. |
Exiting explicitly does the same thing and never returns:
use thru_core::program_utils;
program_utils::succeed(0);program_utils::revert(7);Choose your own error codes. Keep them stable, because clients and indexers surface the raw number.
Revert macros
Section titled “Revert macros”use thru_core::{assert_eq_or_revert, assert_or_revert, revert};
assert_or_revert!(amount > 0, error = 3);assert_eq_or_revert!(account.owner(), &expected_owner, error = 4);revert!(error = 5);Without an explicit error =, assert_or_revert! and assert_eq_or_revert! revert with code 4444.
Panics
Section titled “Panics”thru-core installs a panic handler that logs the panic message with tvm_println! and exits with revert code PANIC_ERROR_CODE (555). Anything that panics in Rust — slice bounds, unwrap, arithmetic overflow in debug profiles — therefore surfaces as 555, not as your own code. Return Err explicitly for expected failures so callers get a meaningful code.
AccountManager panics deliberately in two situations: mutating an account that is currently borrowed, and invoking another program while any borrow is live.
SyscallCode
Section titled “SyscallCode”Syscall wrappers and AccountManager mutators return SyscallCode. Success is 0, SuccessExit is 1, and every error is negative:
| Variant | Value |
|---|---|
BadSegmentTableSize | -7 |
InvalidAccountIndex | -8 |
AccountDoesNotExist | -9 |
AccountNotWritable | -10 |
BalanceOverflow | -11 |
AccountTooBig | -12 |
InvalidObjRefKind | -13 |
ObjNotWritable | -14 |
AccountAlreadyExists | -15 |
BadAccountAddress | -16 |
AccountIsNotProgram | -17 |
AccountHasData | -18 |
SegmentAlreadyMapped | -19 |
BadParams | -20 |
InvalidSegmentId | -21 |
InvalidAddress | -22 |
InvalidStateProof | -23 |
CallDepthTooDeep | -24 |
Revert | -25 |
InsufficientPages | -26 |
InvalidAccount | -27 |
InvalidSegmentSize | -28 |
UnfreeablePage | -29 |
LogDataTooLarge | -30 |
EventTooLarge | -31 |
InvalidProofLen | -32 |
InvalidProofSlot | -33 |
AccountInCompressionTimeout | -34 |
InvalidAccountDataSize | -35 |
InvalidSeedLength | -36 |
TxnHasCompressedAccount | -37 |
InsufficientBalance | -38 |
InvalidOffset | -39 |
ComputeUnitsExceeded | -40 |
InvalidFlags | -41 |
EphemeralAccountCannotCreatePersistent | -42 |
AccountCompressionNotAllowed | -43 |
InvalidSignature | -44 |
AccountUncompressable | -45 |
EventIsZeroSize | -46 |
StateBytesAddedOverflow | -47 |
StateBytesRemovedOverflow | -48 |
ProgramInCallStack | -49 |
Any value the SDK does not recognize maps to UnknownCode. SyscallCode converts to and from i64, so propagating one as a revert code looks like:
if code != SyscallCode::Success { program_utils::revert(code as i64 as u64);}AccountError
Section titled “AccountError”AccountManager::from_txn, get, get_readonly, and account_role return Result<_, AccountError>:
| Variant | Meaning |
|---|---|
IndexOutOfBounds { index, max } | The index is past the transaction’s account count. |
AlreadyBorrowedMutably { index } | An immutable borrow was requested while a mutable borrow is live. |
AlreadyBorrowed { index } | A mutable borrow was requested while another borrow is live. |
TooManyAccountsAccessed { max_capacity } | More than NUM_ACCOUNTS distinct accounts were touched. Raise the const generic. |
UnsupportedTxnVersion { version } | The transaction is not version 1. |
InfoAccessFailed { index, err } | The account metadata or data could not be read; err is a MemoryError. |
MemoryError
Section titled “MemoryError”thru_core::mem accessors return MemoryError::UnsupportedAccountVersion { version } when account metadata is not version 1.
Reserved revert codes
Section titled “Reserved revert codes”| Code | Source |
|---|---|
555 | Rust panic handler. |
4444 | Default code for assert_or_revert! and assert_eq_or_revert!. |
0xBAD0A170 | InvokeAuth magic mismatch. |
0xBAD0A171 | Delegated account is not owned by the calling program. |
0xBAD0A173 | InvokeAuth account index out of range. |
0xBAD0A174 | Authorization check found a delegation whose authorizing program does not own the account. |
Avoid reusing these for your own errors.