Errors and debugging

How TRON API errors are reported, the three error categories (HTTP-level, validation, execution), how to decode hex-encoded validation messages, retry strategy and idempotency, and the debugging endpoints to confirm what actually happened.

TRON API errors fall into three categories with different debugging procedures:

  • HTTP-level errors — network or transport failures before the request reaches the node
  • Validation errors — the node rejected the transaction at validation time, before execution; reported via CONTRACT_VALIDATE_ERROR with a hex-encoded message
  • Execution errors — the transaction was accepted into a block but the on-chain execution failed; reported in the transaction info, not the broadcast response

Each category has different debugging steps and different retry implications. Confusing them is a common production bug; for example, retrying a successfully-broadcast transaction that reverted in execution can produce duplicate balance changes.

📘

Prerequisites


Category 1: HTTP-level errors

The request did not complete a round trip — the client never got a JSON response from the node.

SymptomLikely causeAction
Connection refusedWrong endpoint, node downVerify URL; failover to another node
5xx responseNode-side problem (overload, internal error)Retry with backoff; use a production-grade endpoint (self-hosted node, third-party RPC, or failover pool)
4xx responseBad request URL, missing header, malformed JSONInspect request; fix client
Request timeoutNode slow, network slow, query too expensiveRetry with longer timeout; for heavy queries, split or use TronGrid extension APIs
401, 403API key missing or wrong (TronGrid only)See TronGrid for the API-key flow
429Rate-limited (TronGrid only)Back off; check plan limits

These are transport failures; the transaction was never seen by the node. Safe to retry the same request unchanged — there is no risk of duplicate execution.


Category 2: Validation errors (CONTRACT_VALIDATE_ERROR)

The node received the request, parsed it, but rejected it at the validation stage. The transaction never enters the mempool.

The broadcast response has this shape:

{
  "result": false,
  "code": "CONTRACT_VALIDATE_ERROR",
  "message": "636f6e7472616374207061726168657920646f6573206e6f74206d61746368"
}

The code field tells you the category of failure (CONTRACT_VALIDATE_ERROR, SIGERROR, BANDWIDTH_ERROR, etc.). The actual cause is in messagehex-encoded.

Decoding the hex message

const hex = "636f6e7472616374207061726168657920646f6573206e6f74206d61746368";
const decoded = Buffer.from(hex, "hex").toString("utf8");
// "contract parahey does not match"

Most SDKs decode automatically; if you see raw hex in your logs, you are reading the API response directly. Always decode before alerting or displaying.

Common validation error subtypes

Decoded message (typical)CauseFix
account does not existThe signing or recipient address has never been activated on-chainActivate the account by sending it a small TRX amount first
balance is not sufficientInsufficient TRX for the operationTop up; check fee budget
signature errorSignature verification failedLikely encoding mismatch — see Encoding; also check that raw_data was not modified after construct
Transaction expiredTAPOS reference too old (>~1 minute)Reconstruct; do not retry the same signed transaction
Validate ... contract error, ...Contract-specific validation failedRead the rest of the message; often parameter-shape mismatch
frozenBalance must be positiveStake amount is zero or negativeSend a positive integer in sun
delegateBalance must be more than 1 TRXDelegation below minimumIncrease amount
BANDWIDTH_ERRORSender does not have enough BandwidthTop up Bandwidth or accept TRX burn cost; see Resource model

Safe to retry validation errors only after fixing the underlying cause. Signing a new transaction is always safe; retrying the exact same signed payload only succeeds if the validation environment changed (e.g., balance was topped up).


Category 3: Execution errors

The transaction passed validation, entered the mempool, was included in a block, and then failed at execution time. The broadcast response was successful — the failure shows up only when you query the transaction info. Always query gettransactioninfobyid to verify the final execution state; never treat broadcast success as confirmation that the transaction succeeded on chain.

POST /walletsolidity/gettransactioninfobyid
{ "value": "<txid>" }

Look at the receipt.result field. The full enum (16 values, defined in protocol/src/main/protos/core/Tron.proto):

receipt.resultEnumMeaning
DEFAULT0Initial value before execution completes (you rarely see this on a finalized transaction)
SUCCESS1Transaction executed successfully
REVERT2The TVM reverted (REVERT opcode, assert, require failure)
BAD_JUMP_DESTINATION3Invalid jump in bytecode
OUT_OF_MEMORY4OOM during execution
PRECOMPILED_CONTRACT5Error inside a precompiled contract
STACK_TOO_SMALL6Stack underflow — popped from an empty stack
STACK_TOO_LARGE7Pushed beyond the 1024-item stack limit
ILLEGAL_OPERATION8Illegal opcode or operation
STACK_OVERFLOW9Stack overflow in the VM
OUT_OF_ENERGY10The transaction ran out of Energy mid-execution
OUT_OF_TIME11Execution exceeded the per-transaction time budget
JVM_STACK_OVER_FLOW12JVM-side stack overflow (deep recursion in the VM implementation)
UNKNOWN13Unexpected error not captured by the categories above
TRANSFER_FAILED14An internal TRX or TRC-10 transfer failed
INVALID_CODE15Deployed contract bytecode failed validation

For REVERT, decode the revert reason from the transaction's contractResult[0] field — it follows Solidity's standard Error(string) ABI format.

Execution errors are not retryable as-is — the transaction was burned (Energy / TRX consumed, Bandwidth charged) and the same signed payload cannot be reused (TAPOS will be expired or the network will deduplicate). To recover, build a new transaction with corrected parameters and a fresh signature.


Retry strategy

Different categories have different retry semantics. The summary:

CategorySame payload retryable?Strategy
HTTP-level (network)YesExponential backoff; up to ~30 seconds
5xx node errorYesBackoff; switch endpoint if persistent
Rate-limited (429)YesBackoff respecting Retry-After; consider upgrading plan
Validation signature errorNo — sign error means encoding bug or wrong private keyFix client; reconstruct
Validation Transaction expiredNoReconstruct from scratch with fresh TAPOS
Validation balance not sufficientNo (same payload)Top up balance; reconstruct
Execution REVERT / OUT_OF_ENERGYNoDiagnose; build a corrected transaction

Idempotency

Two safety properties to know:

  • Re-broadcasting the same signed transaction is harmless. The network deduplicates by txID. A duplicate broadcast returns the same txid and does not double-execute. Use this fact during retries: a 5xx after a successful mempool acceptance is recoverable by re-broadcasting.
  • A successful broadcast does not mean a successful execution. Always confirm via gettransactioninfobyid from /walletsolidity/ (the solidified path) before treating the operation as final.

Debugging endpoints

EndpointUse
/wallet/triggerconstantcontractEstimate Energy for a contract call without broadcasting; surfaces revert reasons before paying for execution
/wallet/estimateenergyEnergy estimation (java-tron 4.7.0.1+); more precise than triggerconstantcontract for cost prediction
/walletsolidity/gettransactioninfobyidConfirm execution outcome of a broadcast transaction; reads from solidified data
/wallet/gettransactioninfobyidSame as above but reads from head; available sooner but subject to reorg
/walletsolidity/getblockbynumWalk a specific block's transactions when debugging deposit detection
/wallet/getaccountresourceCheck the sender's Bandwidth, Energy, and fee_limit budget before a large transaction
/wallet/getchainparametersQuery current values of dynamic parameters (e.g., getUnfreezeDelayDays, getEnergyFee) when a transaction fails for parameter-based reasons

For smart-contract development, simulate calls with triggerconstantcontract before committing real transactions. The endpoint itself does not require a transaction-level fee_limit; convert the returned energy_used with the current getEnergyFee and compare it with the fee_limit planned for the real transaction.


Related resources

  • API workflow — what each step in the lifecycle can fail at
  • Encoding — encoding mistakes are the most common validation-error source
  • Resource model — Bandwidth, Energy, fee_limit
  • FAQ — community-curated answers to common errors