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_ERRORwith 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.
| Symptom | Likely cause | Action |
|---|---|---|
| Connection refused | Wrong endpoint, node down | Verify URL; failover to another node |
| 5xx response | Node-side problem (overload, internal error) | Retry with backoff; use a production-grade endpoint (self-hosted node, third-party RPC, or failover pool) |
| 4xx response | Bad request URL, missing header, malformed JSON | Inspect request; fix client |
| Request timeout | Node slow, network slow, query too expensive | Retry with longer timeout; for heavy queries, split or use TronGrid extension APIs |
| 401, 403 | API key missing or wrong (TronGrid only) | See TronGrid for the API-key flow |
| 429 | Rate-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)
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 message — hex-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) | Cause | Fix |
|---|---|---|
account does not exist | The signing or recipient address has never been activated on-chain | Activate the account by sending it a small TRX amount first |
balance is not sufficient | Insufficient TRX for the operation | Top up; check fee budget |
signature error | Signature verification failed | Likely encoding mismatch — see Encoding; also check that raw_data was not modified after construct |
Transaction expired | TAPOS reference too old (>~1 minute) | Reconstruct; do not retry the same signed transaction |
Validate ... contract error, ... | Contract-specific validation failed | Read the rest of the message; often parameter-shape mismatch |
frozenBalance must be positive | Stake amount is zero or negative | Send a positive integer in sun |
delegateBalance must be more than 1 TRX | Delegation below minimum | Increase amount |
BANDWIDTH_ERROR | Sender does not have enough Bandwidth | Top 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.result | Enum | Meaning |
|---|---|---|
DEFAULT | 0 | Initial value before execution completes (you rarely see this on a finalized transaction) |
SUCCESS | 1 | Transaction executed successfully |
REVERT | 2 | The TVM reverted (REVERT opcode, assert, require failure) |
BAD_JUMP_DESTINATION | 3 | Invalid jump in bytecode |
OUT_OF_MEMORY | 4 | OOM during execution |
PRECOMPILED_CONTRACT | 5 | Error inside a precompiled contract |
STACK_TOO_SMALL | 6 | Stack underflow — popped from an empty stack |
STACK_TOO_LARGE | 7 | Pushed beyond the 1024-item stack limit |
ILLEGAL_OPERATION | 8 | Illegal opcode or operation |
STACK_OVERFLOW | 9 | Stack overflow in the VM |
OUT_OF_ENERGY | 10 | The transaction ran out of Energy mid-execution |
OUT_OF_TIME | 11 | Execution exceeded the per-transaction time budget |
JVM_STACK_OVER_FLOW | 12 | JVM-side stack overflow (deep recursion in the VM implementation) |
UNKNOWN | 13 | Unexpected error not captured by the categories above |
TRANSFER_FAILED | 14 | An internal TRX or TRC-10 transfer failed |
INVALID_CODE | 15 | Deployed 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:
| Category | Same payload retryable? | Strategy |
|---|---|---|
| HTTP-level (network) | Yes | Exponential backoff; up to ~30 seconds |
| 5xx node error | Yes | Backoff; switch endpoint if persistent |
| Rate-limited (429) | Yes | Backoff respecting Retry-After; consider upgrading plan |
Validation signature error | No — sign error means encoding bug or wrong private key | Fix client; reconstruct |
Validation Transaction expired | No | Reconstruct from scratch with fresh TAPOS |
Validation balance not sufficient | No (same payload) | Top up balance; reconstruct |
Execution REVERT / OUT_OF_ENERGY | No | Diagnose; 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 sametxidand 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
gettransactioninfobyidfrom/walletsolidity/(the solidified path) before treating the operation as final.
Debugging endpoints
| Endpoint | Use |
|---|---|
/wallet/triggerconstantcontract | Estimate Energy for a contract call without broadcasting; surfaces revert reasons before paying for execution |
/wallet/estimateenergy | Energy estimation (java-tron 4.7.0.1+); more precise than triggerconstantcontract for cost prediction |
/walletsolidity/gettransactioninfobyid | Confirm execution outcome of a broadcast transaction; reads from solidified data |
/wallet/gettransactioninfobyid | Same as above but reads from head; available sooner but subject to reorg |
/walletsolidity/getblockbynum | Walk a specific block's transactions when debugging deposit detection |
/wallet/getaccountresource | Check the sender's Bandwidth, Energy, and fee_limit budget before a large transaction |
/wallet/getchainparameters | Query 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
Updated 7 days ago