Skip to main content

Debugging Contract Errors

To understand how to debug Soroban errors, first we must understand how the errors are associated with each step of the transaction flow, and the likely and common errors in each step of the transaction flow.

General Transaction Flow

The typical transaction submission process can be broken down into the following sequential steps, excluding external interactions like wallet interactions. Each step has its own set of potential errors:

  1. Preflight (optional):

    • What happens: This step involves running a transaction in 'simulation' mode, which records the necessary ledger entries, CPU instructions, required authorizations, etc.
    • Common failures: Preflight is akin to running transactions in Core with maxed-out resources, unrestricted ledger access, and no fees. Therefore, errors typically involve exceeding the network limit or encountering contract logic issues.
  2. Core Accepts the Transaction:

    • What happens: Core evaluates each transaction to decide whether to accept or reject it. Rejected transactions are usually invalid or have insufficient fees, especially during high traffic periods.
    • Common failures: Invalid transactions, such as those with incorrect resource fees or overly high resource values, and invalid footprint are rejected. Other common errors include bad transaction signatures or insufficient funds in the source account. Valid transactions may only be rejected due to a low inclusion fee.
  3. Core Includes the Transaction in the Ledger:

    • What happens: Accepted transactions remain in Core’s memory until they are either included in the ledger or evicted.
    • Common failures: Transactions may fail to be included in the ledger if they have a low inclusion fee and need to be evicted during traffic surges to accommodate more transactions.
  4. Core Applies the Transaction to the Ledger:

    • What happens: Core executes the included transaction.
    • Common failures: This step has the widest range of potential errors. Failures could include accessing archived entries, resource depletion, accessing entries outside the specified footprint, or encountering logic failures within the contract.

For more information about fees, please visit Fees, Resource Limits, and Metering.

Detailed Soroban Errors

1. Preflight

Errors here are returned by the host and propagated through RPC. This doesn’t cover other possible errors (e.g. network errors, errors in RPC itself etc.)

ErrorExplanationFix
HostError(Budget, LimitExceeded)A network-defined resource limit has been exceeded (either instructions, or memory). Refer to diagnostic events to check which limit has been exceeded (99% of the time this will be instructions).Optimize the contract to consume fewer resources.
HostError(Storage, MissingValue)Trying to access a ledger entry that does not exist. 99.9% of the time this means that either contract, or Wasm does not exist in the ledger (the remaining cases can only appear when the developer doesn’t use the Soroban SDK for contracts). Diagnostic events should indicate which entry is missing.Deploy the respective contract or Wasm.
HostError(WasmVm, InvalidAction)There was a failure in some Wasm contract, typically a panic!(). Diagnostic events might provide more detailed information, but not always, as the panic!() messages are not included in the Wasm builds.Fix the contract logic or invocation arguments. Since the most typical reason for encountering this is panic!(), it might be a good idea to use panic_with_error!() instead of panic!() everywhere. Writing more unit tests is recommended.
HostError(<some other code>)An arbitrary execution error, like accessing a value out of container bounds, overflow in i128 arithmetics, incorrect invocation argument type etc. The error code should provide a general idea of what is failing, but refer to diagnostic events for details.Fix the contract logic or invocation arguments. Additional debugging can be done via unit tests.

2. Core Accepts the Transaction

The error is returned by the core immediately as a response to the transaction being sent and surfaced to the user through RPC. There are a few error-related fields in the Core’s response:

  • status contains one of the few coarse codes: it’s either “ERROR” for any transaction validation error, or one of the few special non-validation-related statuses.
  • result contains the encoded TransactionResult XDR that, in case of “ERROR” status, will contain the transaction-level error code starting with “tx”, such as txMALFORMED.
  • txFAILED transaction errors at this stage correspond to the operation-specific validation errors, so the operation result code should be examined.
  • diagnostics will contain additional error information in the Soroban diagnostic event format. This typically will be returned for txSOROBAN_INVALID transaction errors but may be used more extensively in the future.
Error from CoreExplanationFix
status: TRY_AGAIN_LATERThere is already a transaction from the same source account in memory. The transaction has been rejected due to too low inclusion fee and has been resubmitted too soon.Wait for the previous transaction to be applied and resubmit, or switch to channel accounts if higher throughput is needed. Wait more time before re-submitting the transaction. Build a transaction within the network limits.
status: ERROR, error: txMALFORMEDTransaction is fundamentally wrong: Soroban operation is not the only operation in the transaction; Soroban extension is missing; Fee or resource fee is negative; Resource fee is greater than tx fee; Declared resources are higher than network-wide limits (e.g. it wants to use 200M instructions, while the ledger-wide limit is just 100M).Make sure the transaction is well-formed and the total transaction fee is high enough.
status: ERROR, error: txINSUFFICIENT_FEEMost likely: The inclusion fee is too low during the traffic surge. Unlikely: The inclusion fee is lower than the network minimum (i.e., lower than 100 stroops).Bump the transaction fee or wait until there is less traffic.
status: ERROR, error: txSOROBAN_INVALID, diagnostics: “transaction $RESOURCE_NAME resources exceed network config limit” or similarSome resource value specified in the transaction exceeds the network limit. For example, this would trigger if the transaction specifies 200M instructions, while the network limit is just 100M. Note, that this has nothing to do with what the transaction actually does. Only resource declarations are examined at this point.Optimize the contract to fit into the resource limit and specify the respective value in transaction (typically by running preflight). In the case if there is just too big a ‘safety margin’ for the resources (e.g. +20% given that the transaction already needs 90% of the network limit), reduce the resource declaration to fit the limit.
status: ERROR, error: txSOROBAN_INVALID, diagnostics: footprint-related messageThere are a number of footprint requirements, such as it shouldn’t contain duplicate keys, shouldn’t contain entries unsupported by Soroban etc. Diagnostic message will specify the details of which requirement has been violated.Fix the footprint. This should only occur if the footprint has been built or modified manually; preflight footprints should always pass all checks.
status: ERROR, error: txSOROBAN_INVALID, diagnostics: "transaction sorobanData.resourceFee is lower than the actual Soroban resource fee"The resource fee specified in the transaction is not sufficient to cover the resources specifiedin the transaction. Note, that this has nothing to do with what the transaction actually does. Only resource declarations are examined at this point.Increase the resource fee. This should normally only occur when resources are computed or modified manually. Preflight always computes the sufficient resource fee.
status: ERROR, error: txFAILED, operation error: EXTEND_FOOTPRINT_TTL_MALFORMEDOne of the footprint requirements for ExtendFootprintTTL operation has not been fulfilled: - Only Soroban ledger entries can be extended. - Only readOnly footprint should be populated. - The TTL extension should be not larger than the maximum allowed TTL extension.Make sure that footprints conform to the requirements. Ideally, client-side libraries should ensure that the TTL extension transactions are well-formed.
status: ERROR, error: txFAILED, operation error: RESTORE_FOOTPRINT_MALFORMEDOne of the footprint requirements for RestoreFootprint operation has not been fulfilled: Only persistent Soroban ledger entries can be restored; Only readWrite footprint should be populated.Make sure that footprints conform to the requirements. Ideally, client-side libraries should ensure that the restoration transactions are well-formed.
status: ERROR, error: tx$CODEThe remaining error codes have the same semantics for Soroban as they do for Stellar; these include errors like insufficient account balance, bad seq num, etc. These errors are usually straightforward to interpret according to the name.Fix the issue corresponding to the code. There is nothing Soroban specific here.

3. Core Includes the Transaction in the Ledger

There are instances when the Core does not appear to include the transaction in the ledger, The following best practice is recommended:

ErrorExplanationFix
Transaction appears ‘stuck’ and is never appliedThere is no direct error reporting here because the network can’t reasonably communicate which transaction that it drops (E.g. Node A has dropped the transaction doesn’t necessarily mean that some other Node B has dropped it). When querying against a data endpoint, the transaction might be reported as ‘pending’.Introduce transaction time bounds on the client side. If a transaction hasn’t been applied within 1-2 minutes it’s highly unlikely that the network still remembers it. It is strongly recommended thus that all transactions should include a time bound or ledger bound. Having a bound allows the developer to re-submit the transaction after the time bound is exceeded and optionally bump the fee if the error persists.
info

It is strongly recommended that all transactions should include a time bound or ledger bound.

4. Core Applies the Transaction to the Ledger

The errors and diagnostic events are recorded in the transaction meta stream emitted by the Core instance when the ledger is being closed. Then, the RPC ingests the transaction metadata (tx meta) and allows developers to query the tx meta.

When an error occurs, the Core stores a few error-related fields in the transaction result meta:

  • The transaction result (error) will be txFAILED for Soroban-related failures (or any operation failures in general). It is sometimes possible to get other tx$ERROR errors (such as txBAD_AUTH), but these are not related to Soroban and are more of an edge case.
  • The operation error will be one of INVOKE_HOST_FUNCTION_$ERROR, RESTORE_FOOTPRINT_$ERROR, and EXTEND_FOOTPRINT_TTL_$ERROR (corresponding to InvokeHostFunction, RestoreFootprint, and ExtendFootprintTTL operations). The operation errors are not very granular, and diagnostic events should typically be used to understand the exact error.
  • If the Core instance producing meta has Soroban diagnostics enabled (which it usually should), the meta will also contain diagnostic events with more detailed error information.
  • Note that there might be a few gaps in diagnostic event coverage. Since this is not a protocol change, Core may add more diagnostic events in future releases.

Note: $OPERATION here refers to any one of the Soroban operations: INVOKE_HOST_FUNCTION, RESTORE_FOOTPRINT, and EXTEND_FOOTPRINT_TTL.

Error and DiagnosticsExplanationFix
$OPERATION_RESOURCE_LIMIT_EXCEEDED, diagnostics: message specifying which resource limit has been exceededThe transaction has exceeded the resource limit during execution. For most resources (instructions, read bytes, write bytes), this has nothing to do with the network limit; it's the transaction-specified limit. For example, a transaction has 10M instructions specified but consumes at least 10M + 1 instruction and immediately fails. Diagnostic events specify which limit has been exceeded and contain both the limit itself and the value transaction tried to consume.If exceeding the transaction-specified limit, increase the respective resource declared in the transaction. If preflight determined the resource limits, likely reason for exceeding is logic dependent on volatile ledger state (e.g., RNG or ledger sequence). Ensure logic is estimated sufficiently by preflight. If exceeding network limits (entry size, memory), modify/ optimize the contract.
$OPERATION_INSUFFICIENT_REFUNDABLE_FEEThe refundable resource fee was not sufficient to cover the consumed refundable resources, i.e., not enough to pay for emitted events or TTL extensions.The simplest fix is to increase the resource fee unconditionally; unspent fees are refunded. Preflight estimates refundable fees but can't predict TTL extensions' actual cost due to ledger state volatility. Thus preflight can’t always predict the fee that will actually be charged.
INVOKE_HOST_FUNCTION_ENTRY_ARCHIVEDTransaction tries to access an archived ledger entry, e.g. when the footprint contains a ledger key for an archived persistent Soroban ledger entry. Note, this failure happens before the Soroban host is even created, so this is unrelated to contract logic.Restore the archived entry using RestoreFootprintOp operation. Preflight usually reports archived entries, but an entry may be archived after the preflight simulation happened.
INVOKE_HOST_FUNCTION_TRAPPED, diagnostics: HostError(Storage, LimitExceeded).Host function tried to access a ledger entry outside of the footprint. Diagnostic events specify the exact missing entry. Preflight should usually return the valid footprint, so in the case when preflight is being used, it’s likely that: a) Contract generates non-deterministic keys (e.g. derives them from RNG or ledger sequence), b) Contract has non-deterministic logic that results in access to a different entry set (e.g. a ‘lottery’ contract that non-deterministically performs transfer to either address A or address B).If preflight not used, fix client code to build the correct footprint. If using preflight, fix contract or footprint: avoid non-deterministic keys , and add all keys that can be accessed in any scenario, (e.g. balance entries for both A and B in the ‘lottery’ contract example).
INVOKE_HOST_FUNCTION_TRAPPED, diagnostics: HostError(WasmVm, InvalidAction)Failure in a Wasm contract, typically a panic!(). Diagnostic events might provide more detailed information, but not always, as the panic!() messages are not included in the Wasm builds.Fix contract logic. Consider using panic_with_error!() and increase preflight runs and unit testing.
INVOKE_HOST_FUNCTION_TRAPPED, diagnostics: HostError(Auth, InvalidAction) "Unauthorized function call for address <'ADDRESS'>"The transaction didn’t have an authorization payload necessary to satisfy the require_auth host function call for a given <’ADDRESS’>. This is unlikely to occur when preflight is being used, but still possible if the require_auth arguments depend on volatile ledger state (e.g. if require_auth contains the ledger sequence number)Attach proper authorization payload to the transaction, usually via preflight. Ensure auth payloads are deterministic and depend only on invocation arguments.
INVOKE_HOST_FUNCTION_TRAPPED, diagnostics: HostError(Auth, <'error code'>)Authentication error, like missing/expired/invalid signature or reused nonce. These errors won’t always appear in the preflight unless the whole signed auth payload is used in the preflight request. The diagnostic events will also have a more detailed explanation of what the error was.Fix authentication payload according to error (e.g. use proper signature, new nonce etc.). Consider preflight with all signatures for faster debugging.
INVOKE_HOST_FUNCTION_TRAPPED, HostError(<'some other code'>)An arbitrary execution error, like accessing a value out of container bounds, overflow in i128 arithmetics, incorrect invocation argument type etc. The error code should provide a general idea of what is failing, but refer to diagnostic events for more details.Fix contract logic or invocation arguments. Additional debugging can be done via more preflight runs and unit tests.